device/pciexp: Add support for PCIe CLK power management

Set PCIe "Enable Clock Power Management", if endpoint supports it.

BUG=chrome-os-partner:31424
BRANCH=none
TEST=build and boot on rambi, check Enable Clock Power Management
     in link control register is set properly

Change-Id: Ie54110d1ef42184cfcf47c9fe4d735960aebe47f
Signed-off-by: Kane Chen <kane.chen@intel.com>
Reviewed-on: https://chromium-review.googlesource.com/220742
Reviewed-by: Duncan Laurie <dlaurie@chromium.org>
[Edit commit message.]
Signed-off-by: Paul Menzel <paulepanter@users.sourceforge.net>
Reviewed-on: http://review.coreboot.org/8447
Tested-by: build bot (Jenkins)
Reviewed-by: Kyösti Mälkki <kyosti.malkki@gmail.com>
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
diff --git a/src/device/Kconfig b/src/device/Kconfig
index 91da02a..42a68d2 100644
--- a/src/device/Kconfig
+++ b/src/device/Kconfig
@@ -251,6 +251,14 @@
 	help
 	  Detect and enable ASPM on PCIe links.
 
+config PCIEXP_CLK_PM
+	prompt "Enable PCIe Clock Power Management"
+	bool
+	depends on PCIEXP_PLUGIN_SUPPORT
+	default n
+	help
+	  Detect and enable Clock Power Management on PCIe.
+
 config PCI_BUS_SEGN_BITS
 	int
 	default 0
diff --git a/src/device/pciexp_device.c b/src/device/pciexp_device.c
index 87aea67..edb103d 100644
--- a/src/device/pciexp_device.c
+++ b/src/device/pciexp_device.c
@@ -90,6 +90,23 @@
 }
 #endif /* CONFIG_PCIEXP_COMMON_CLOCK */
 
+#if CONFIG_PCIEXP_CLK_PM
+static void pciexp_enable_clock_power_pm(device_t endp, unsigned endp_cap)
+{
+	/* check if per port clk req is supported in device */
+	u32 endp_ca;
+	u16 lnkctl;
+	endp_ca = pci_read_config32(endp, endp_cap + PCI_EXP_LNKCAP);
+	if ((endp_ca & PCI_EXP_CLK_PM) == 0) {
+		printk(BIOS_INFO, "PCIE CLK PM is not supported by endpoint");
+		return;
+	}
+	lnkctl = pci_read_config16(endp, endp_cap + PCI_EXP_LNKCTL);
+	lnkctl = lnkctl | PCI_EXP_EN_CLK_PM;
+	pci_write_config16(endp, endp_cap + PCI_EXP_LNKCTL, lnkctl);
+}
+#endif /* CONFIG_PCIEXP_CLK_PM */
+
 #if CONFIG_PCIEXP_ASPM
 /*
  * Determine the ASPM L0s or L1 exit latency for a link
@@ -200,6 +217,11 @@
 	pciexp_enable_common_clock(root, root_cap, dev, cap);
 #endif
 
+#if CONFIG_PCIEXP_CLK_PM
+	/* Check if per port CLK req is supported by endpoint*/
+	pciexp_enable_clock_power_pm(dev, cap);
+#endif
+
 #if CONFIG_PCIEXP_ASPM
 	/* Check for and enable ASPM */
 	enum aspm_type apmc = pciexp_enable_aspm(root, root_cap, dev, cap);
diff --git a/src/include/device/pci_def.h b/src/include/device/pci_def.h
index 00d68ad..4039785 100644
--- a/src/include/device/pci_def.h
+++ b/src/include/device/pci_def.h
@@ -375,9 +375,11 @@
 #define  PCI_EXP_LNKCAP_ASPMS	0xc00	/* ASPM Support */
 #define  PCI_EXP_LNKCAP_L0SEL	0x7000	/* L0s Exit Latency */
 #define  PCI_EXP_LNKCAP_L1EL	0x38000	/* L1 Exit Latency */
+#define  PCI_EXP_CLK_PM		0x40000	/* Clock Power Management */
 #define PCI_EXP_LNKCTL		16	/* Link Control */
 #define  PCI_EXP_LNKCTL_RL	0x20	/* Retrain Link */
 #define  PCI_EXP_LNKCTL_CCC	0x40	/* Common Clock COnfiguration */
+#define  PCI_EXP_EN_CLK_PM	0x100	/* Enable Clock Power Management */
 #define PCI_EXP_LNKSTA		18	/* Link Status */
 #define  PCI_EXP_LNKSTA_LT	0x800	/* Link Training */
 #define  PCI_EXP_LNKSTA_SLC	0x1000	/* Slot Clock Configuration */