mb/lenovo: Support dual graphics for xx20/xx30 ThinkPads

Add CMOS option that allows to use both integrated and discrete GPU.

Tested on ThinkPad W530.

Change-Id: I8842fef0fa1235eb91abf6b7e655ed4d8598adc7
Signed-off-by: Evgeny Zinoviev <me@ch1p.com>
Reviewed-on: https://review.coreboot.org/28393
Reviewed-by: Patrick Rudolph <siro@das-labor.org>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
diff --git a/src/drivers/lenovo/hybrid_graphics/romstage.c b/src/drivers/lenovo/hybrid_graphics/romstage.c
index 8814916..35cb384 100644
--- a/src/drivers/lenovo/hybrid_graphics/romstage.c
+++ b/src/drivers/lenovo/hybrid_graphics/romstage.c
@@ -20,7 +20,6 @@
 #include <southbridge/intel/common/gpio.h>
 #include <ec/lenovo/pmh7/pmh7.h>
 #include <console/console.h>
-#include <delay.h>
 
 #include "hybrid_graphics.h"
 #include "chip.h"
@@ -87,18 +86,9 @@
 			set_gpio(config->dgpu_power_gpio,
 				 config->dgpu_power_off_lvl);
 	} else if (config->has_thinker1) {
-		const size_t power_en = !!(pmh7_register_read(0x50) & 0x08);
-		if (*enable_peg && !power_en) {
-			pmh7_register_clear_bit(0x50, 7); // DGPU_RST
-			pmh7_register_set_bit(0x50, 3); // DGPU_PWR
-			mdelay(10);
-			pmh7_register_set_bit(0x50, 7); // DGPU_RST
-			mdelay(50);
-		} else if (!*enable_peg && power_en) {
-			pmh7_register_clear_bit(0x50, 7); // DGPU_RST
-			udelay(100);
-			pmh7_register_clear_bit(0x50, 3); // DGPU_PWR
-		}
+		bool power_en = pmh7_dgpu_power_state();
+		if (*enable_peg != power_en)
+			pmh7_dgpu_power_enable(!power_en);
 	} else {
 		printk(BIOS_ERR, "Hybrid graphics:"
 		       " FIXME: dGPU power handling not implemented\n");
diff --git a/src/ec/lenovo/pmh7/pmh7.c b/src/ec/lenovo/pmh7/pmh7.c
index b160fd0..f7b798b 100644
--- a/src/ec/lenovo/pmh7/pmh7.c
+++ b/src/ec/lenovo/pmh7/pmh7.c
@@ -19,6 +19,7 @@
 #include <device/pnp.h>
 #include <stdlib.h>
 #include <pc80/mc146818rtc.h>
+#include <delay.h>
 
 #include "pmh7.h"
 #include "chip.h"
@@ -64,6 +65,26 @@
 		pmh7_register_set_bit(0x62, 0);
 }
 
+void pmh7_dgpu_power_enable(int onoff)
+{
+	if (onoff) {
+		pmh7_register_clear_bit(0x50, 7); // DGPU_RST
+		pmh7_register_set_bit(0x50, 3); // DGPU_PWR
+		mdelay(10);
+		pmh7_register_set_bit(0x50, 7); // DGPU_RST
+		mdelay(50);
+	} else {
+		pmh7_register_clear_bit(0x50, 7); // DGPU_RST
+		udelay(100);
+		pmh7_register_clear_bit(0x50, 3); // DGPU_PWR
+	}
+}
+
+bool pmh7_dgpu_power_state(void)
+{
+	return (pmh7_register_read(0x50) & 0x08) == 8;
+}
+
 void pmh7_register_set_bit(int reg, int bit)
 {
 	char val;
diff --git a/src/ec/lenovo/pmh7/pmh7.h b/src/ec/lenovo/pmh7/pmh7.h
index d486ceb..a641fce 100644
--- a/src/ec/lenovo/pmh7/pmh7.h
+++ b/src/ec/lenovo/pmh7/pmh7.h
@@ -35,5 +35,7 @@
 void pmh7_touchpad_enable(int onoff);
 void pmh7_ultrabay_power_enable(int onoff);
 void pmh7_trackpoint_enable(int onoff);
+void pmh7_dgpu_power_enable(int onoff);
+bool pmh7_dgpu_power_state(void);
 
 #endif /* EC_LENOVO_PMH7_H */
diff --git a/src/mainboard/lenovo/t420/cmos.layout b/src/mainboard/lenovo/t420/cmos.layout
index 5a9e570..f55c203 100644
--- a/src/mainboard/lenovo/t420/cmos.layout
+++ b/src/mainboard/lenovo/t420/cmos.layout
@@ -129,6 +129,7 @@
 11    6     224M
 12    0     Integrated Only
 12    1     Discrete Only
+12    2     Dual Graphics
 13    0     Disable
 13    1     AC and battery
 13    2     AC only
diff --git a/src/mainboard/lenovo/t420/romstage.c b/src/mainboard/lenovo/t420/romstage.c
index 36e83a3..bef2562 100644
--- a/src/mainboard/lenovo/t420/romstage.c
+++ b/src/mainboard/lenovo/t420/romstage.c
@@ -30,6 +30,9 @@
 
 	early_hybrid_graphics(&igd, &peg);
 
+	if (peg && igd)
+		return;
+
 	/* Hide disabled devices */
 	reg32 = pci_read_config32(PCI_DEV(0, 0, 0), DEVEN);
 	reg32 &= ~(DEVEN_PEG10 | DEVEN_IGD);
diff --git a/src/mainboard/lenovo/t420s/cmos.layout b/src/mainboard/lenovo/t420s/cmos.layout
index f395c0c..2be55f6 100644
--- a/src/mainboard/lenovo/t420s/cmos.layout
+++ b/src/mainboard/lenovo/t420s/cmos.layout
@@ -129,6 +129,7 @@
 11    6     224M
 12    0     Integrated Only
 12    1     Discrete Only
+12    2     Dual Graphics
 13    0     Disable
 13    1     AC and battery
 13    2     AC only
diff --git a/src/mainboard/lenovo/t420s/romstage.c b/src/mainboard/lenovo/t420s/romstage.c
index 55011cf2..be8052c 100644
--- a/src/mainboard/lenovo/t420s/romstage.c
+++ b/src/mainboard/lenovo/t420s/romstage.c
@@ -32,6 +32,9 @@
 
 	early_hybrid_graphics(&igd, &peg);
 
+	if (peg && igd)
+		return;
+
 	/* Hide disabled devices */
 	reg32 = pci_read_config32(PCI_DEV(0, 0, 0), DEVEN);
 	reg32 &= ~(DEVEN_PEG10 | DEVEN_IGD);
diff --git a/src/mainboard/lenovo/t430/cmos.layout b/src/mainboard/lenovo/t430/cmos.layout
index 2b687c2..1b50e7f 100644
--- a/src/mainboard/lenovo/t430/cmos.layout
+++ b/src/mainboard/lenovo/t430/cmos.layout
@@ -128,6 +128,7 @@
 11    6     224M
 12    0     Integrated Only
 12    1     Discrete Only
+12    2     Dual Graphics
 13    0     Disable
 13    1     AC and battery
 13    2     AC only
diff --git a/src/mainboard/lenovo/t430/romstage.c b/src/mainboard/lenovo/t430/romstage.c
index 94679df..90d0886 100644
--- a/src/mainboard/lenovo/t430/romstage.c
+++ b/src/mainboard/lenovo/t430/romstage.c
@@ -30,6 +30,9 @@
 
 	early_hybrid_graphics(&igd, &peg);
 
+	if (peg && igd)
+		return;
+
 	/* Hide disabled devices */
 	reg32 = pci_read_config32(PCI_DEV(0, 0, 0), DEVEN);
 	reg32 &= ~(DEVEN_PEG10 | DEVEN_IGD);
diff --git a/src/mainboard/lenovo/t430s/cmos.default b/src/mainboard/lenovo/t430s/cmos.default
index 979f132..3e1f130 100644
--- a/src/mainboard/lenovo/t430s/cmos.default
+++ b/src/mainboard/lenovo/t430s/cmos.default
@@ -13,4 +13,5 @@
 sticky_fn=Disable
 trackpoint=Enable
 backlight=Both
+enable_dual_graphics=Disable
 usb_always_on=Disable
diff --git a/src/mainboard/lenovo/t430s/cmos.layout b/src/mainboard/lenovo/t430s/cmos.layout
index 538e624..97d97ed 100644
--- a/src/mainboard/lenovo/t430s/cmos.layout
+++ b/src/mainboard/lenovo/t430s/cmos.layout
@@ -76,7 +76,8 @@
 
 # coreboot config options: northbridge
 432          3       e       11       gfx_uma_size
-#435         5       r       0        unused
+435          1       e       1        enable_dual_graphics
+#436         4       r       0        unused
 440          8       h       0        volume
 
 # SandyBridge MRC Scrambler Seed values
diff --git a/src/mainboard/lenovo/t430s/romstage.c b/src/mainboard/lenovo/t430s/romstage.c
index 3f6d9f2..633ba3f 100644
--- a/src/mainboard/lenovo/t430s/romstage.c
+++ b/src/mainboard/lenovo/t430s/romstage.c
@@ -15,12 +15,14 @@
  * GNU General Public License for more details.
  */
 
+#include <option.h>
 #include <arch/byteorder.h>
 #include <arch/io.h>
 #include <device/pci_def.h>
 #include <console/console.h>
 #include <northbridge/intel/sandybridge/raminit_native.h>
 #include <southbridge/intel/bd82x6x/pch.h>
+#include <ec/lenovo/pmh7/pmh7.h>
 
 void pch_enable_lpc(void)
 {
@@ -64,6 +66,22 @@
 }
 
 void mainboard_early_init(int s3resume) {
+	u8 enable_peg;
+	if (get_option(&enable_peg, "enable_dual_graphics") != CB_SUCCESS)
+		enable_peg = 0;
+
+	bool power_en = pmh7_dgpu_power_state();
+
+	if (enable_peg != power_en)
+		pmh7_dgpu_power_enable(!power_en);
+
+	if (!enable_peg) {
+		// Hide disabled dGPU device
+		u32 reg32 = pci_read_config32(PCI_DEV(0, 0, 0), DEVEN);
+		reg32 &= ~DEVEN_PEG10;
+
+		pci_write_config32(PCI_DEV(0, 0, 0), DEVEN, reg32);
+	}
 }
 
 void mainboard_config_superio(void)
diff --git a/src/mainboard/lenovo/t520/romstage.c b/src/mainboard/lenovo/t520/romstage.c
index 328ae37..4fcd651 100644
--- a/src/mainboard/lenovo/t520/romstage.c
+++ b/src/mainboard/lenovo/t520/romstage.c
@@ -42,6 +42,9 @@
 
 	early_hybrid_graphics(&igd, &peg);
 
+	if (peg && igd)
+		return;
+
 	/* Hide disabled devices */
 	reg32 = pci_read_config32(PCI_DEV(0, 0, 0), DEVEN);
 	reg32 &= ~(DEVEN_PEG10 | DEVEN_IGD);
diff --git a/src/mainboard/lenovo/t530/cmos.layout b/src/mainboard/lenovo/t530/cmos.layout
index 9a099f5..3400a4d 100644
--- a/src/mainboard/lenovo/t530/cmos.layout
+++ b/src/mainboard/lenovo/t530/cmos.layout
@@ -129,6 +129,7 @@
 11    6     224M
 12    0     Integrated Only
 12    1     Discrete Only
+12    2     Dual Graphics
 13    0     Disable
 13    1     AC and battery
 13    2     AC only
diff --git a/src/mainboard/lenovo/t530/romstage.c b/src/mainboard/lenovo/t530/romstage.c
index ba7a229..88e07c1 100644
--- a/src/mainboard/lenovo/t530/romstage.c
+++ b/src/mainboard/lenovo/t530/romstage.c
@@ -34,6 +34,9 @@
 
 	early_hybrid_graphics(&igd, &peg);
 
+	if (peg && igd)
+		return;
+
 	/* Hide disabled devices */
 	reg32 = pci_read_config32(PCI_DEV(0, 0, 0), DEVEN);
 	reg32 &= ~(DEVEN_PEG10 | DEVEN_IGD);