public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH] x86: new Intel Atom SoC power management controller driver
@ 2014-03-06  3:31 Li, Aubrey
  2014-03-06  3:49 ` H. Peter Anvin
  0 siblings, 1 reply; 13+ messages in thread
From: Li, Aubrey @ 2014-03-06  3:31 UTC (permalink / raw)
  To: Matthew Garrett, H. Peter Anvin, mingo, tglx, linux-kernel,
	alan@linux.intel.com

The Power Management Controller (PMC) controls many of the power
management features present in the SoC. This driver provides
interface to configure the Power Management Controller (PMC).

This driver exposes PMC device state and sleep state residency
via debugfs for observation:
	/sys/kernel/debugfs/pmc_atom/dev_state
	/sys/kernel/debugfs/pmc_atom/sleep_state

This driver also provides a native power off function via PMC PCI
IO port.

Signed-off-by: Aubrey Li <aubrey.li@intel.com>
---
 arch/x86/Kconfig                |    4 +
 arch/x86/include/asm/pmc_atom.h |   91 ++++++++++++
 arch/x86/kernel/Makefile        |    1 +
 arch/x86/kernel/pmc_atom.c      |  301
+++++++++++++++++++++++++++++++++++++++
 4 files changed, 397 insertions(+)
 create mode 100644 arch/x86/include/asm/pmc_atom.h
 create mode 100644 arch/x86/kernel/pmc_atom.c

diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 0af5250..18f7d38 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -2420,6 +2420,10 @@ config IOSF_MBI
 	  Fabric (IOSF) Sideband MailBox Interface (MBI). For MBI platforms
 	  enumerable by PCI.

+config PMC_ATOM
+	def_bool y
+        depends on PCI
+
 source "net/Kconfig"

 source "drivers/Kconfig"
diff --git a/arch/x86/include/asm/pmc_atom.h
b/arch/x86/include/asm/pmc_atom.h
new file mode 100644
index 0000000..6802d17
--- /dev/null
+++ b/arch/x86/include/asm/pmc_atom.h
@@ -0,0 +1,91 @@
+/*
+ * Intel Atom SOC Power Management Controller Header File
+ * Copyright (c) 2014, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ */
+
+#ifndef PMC_ATOM_H
+#define PMC_ATOM_H
+
+/* ValleyView Power Control Unit PCI Device ID */
+#define	PCI_DEVICE_ID_VLV_PMC	0x0F1C
+
+/* PMC Memory mapped IO registers */
+#define	PMC_BASE_ADDR_OFFSET	0x44
+#define	PMC_BASE_ADDR_MASK	0xFFFFFE00
+#define	PMC_MMIO_REG_LEN	0x100
+#define	PMC_REG_BIT_WIDTH	32
+
+/* BIOS uses FUNC_DIS to disable specific function */
+#define	PMC_FUNC_DIS		0x34
+#define	PMC_FUNC_DIS_2		0x38
+/* The timers acumulate time spent in sleep state */
+#define	PMC_S0IR_TMR		0x80
+#define	PMC_S0I1_TMR		0x84
+#define	PMC_S0I2_TMR		0x88
+#define	PMC_S0I3_TMR		0x8C
+#define	PMC_S0_TMR		0x90
+/* Sleep state counter is in units of of 32us */
+#define	PMC_TMR_SHIFT		5
+
+/* These registers reflect D3 status of functions */
+#define	PMC_D3_STS_0		0xA0
+
+#define	BIT_LPSS1_F0_DMA	(1 << 0)
+#define	BIT_LPSS1_F1_PWM1	(1 << 1)
+#define	BIT_LPSS1_F2_PWM2	(1 << 2)
+#define	BIT_LPSS1_F3_HSUART1	(1 << 3)
+#define	BIT_LPSS1_F4_HSUART2	(1 << 4)
+#define	BIT_LPSS1_F5_SPI	(1 << 5)
+#define	BIT_LPSS1_F6_XXX	(1 << 6)
+#define	BIT_LPSS1_F7_XXX	(1 << 7)
+#define	BIT_SCC_EMMC		(1 << 8)
+#define	BIT_SCC_SDIO		(1 << 9)
+#define	BIT_SCC_SDCARD		(1 << 10)
+#define	BIT_SCC_MIPI		(1 << 11)
+#define	BIT_HDA			(1 << 12)
+#define	BIT_LPE			(1 << 13)
+#define	BIT_OTG			(1 << 14)
+#define	BIT_USH			(1 << 15)
+#define	BIT_GBE			(1 << 16)
+#define	BIT_SATA		(1 << 17)
+#define	BIT_USB_EHCI		(1 << 18)
+#define	BIT_SEC			(1 << 19)
+#define	BIT_PCIE_PORT0		(1 << 20)
+#define	BIT_PCIE_PORT1		(1 << 21)
+#define	BIT_PCIE_PORT2		(1 << 22)
+#define	BIT_PCIE_PORT3		(1 << 23)
+#define	BIT_LPSS2_F0_DMA	(1 << 24)
+#define	BIT_LPSS2_F1_I2C1	(1 << 25)
+#define	BIT_LPSS2_F2_I2C2	(1 << 26)
+#define	BIT_LPSS2_F3_I2C3	(1 << 27)
+#define	BIT_LPSS2_F4_I2C4	(1 << 28)
+#define	BIT_LPSS2_F5_I2C5	(1 << 29)
+#define	BIT_LPSS2_F6_I2C6	(1 << 30)
+#define	BIT_LPSS2_F7_I2C7	(1 << 31)
+
+#define	PMC_D3_STS_1		0xA4
+#define	BIT_SMB			(1 << 0)
+#define	BIT_USH_SS_PHY		(1 << 1)
+#define	BIT_OTG_SS_PHY		(1 << 2)
+#define	BIT_DFX			(1 << 3)
+
+/* PMC I/O Registers */
+#define	ACPI_BASE_ADDR_OFFSET	0x40
+#define	ACPI_BASE_ADDR_MASK	0xFFFFFE00
+#define	ACPI_MMIO_REG_LEN	0x100
+
+#define	PM1_CNT			0x4
+#define	SLEEP_TYPE_MASK		0xFFFFECFF
+#define	SLEEP_TYPE_S5		0x1C00
+#define	SLEEP_ENABLE		0x2000
+#endif /* PMC_ATOM_H */
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
index cb648c8..b71a61a 100644
--- a/arch/x86/kernel/Makefile
+++ b/arch/x86/kernel/Makefile
@@ -104,6 +104,7 @@ obj-$(CONFIG_EFI)			+= sysfb_efi.o
 obj-$(CONFIG_PERF_EVENTS)		+= perf_regs.o
 obj-$(CONFIG_TRACING)			+= tracepoint.o
 obj-$(CONFIG_IOSF_MBI)			+= iosf_mbi.o
+obj-$(CONFIG_PMC_ATOM)			+= pmc_atom.o

 ###
 # 64 bit specific files
diff --git a/arch/x86/kernel/pmc_atom.c b/arch/x86/kernel/pmc_atom.c
new file mode 100644
index 0000000..46ece98
--- /dev/null
+++ b/arch/x86/kernel/pmc_atom.c
@@ -0,0 +1,301 @@
+/*
+ * Intel Atom SOC Power Management Controller Driver
+ * Copyright (c) 2014, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/device.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/io.h>
+
+#include <asm/pmc_atom.h>
+
+#define	DRIVER_NAME "pmc_atom"
+
+struct pmc_dev {
+	struct pci_dev *pdev;
+	u32 base_addr;
+	void __iomem *regmap;
+#ifdef CONFIG_DEBUG_FS
+	struct dentry *dbgfs_dir;
+#endif /* CONFIG_DEBUG_FS */
+};
+
+static u32 acpi_base_addr;
+
+struct pmc_dev_map {
+	char *name;
+	u32 bit_mask;
+};
+
+static struct pmc_dev_map dev_map[] = {
+	{"0  - LPSS1_F0_DMA",		BIT_LPSS1_F0_DMA},
+	{"1  - LPSS1_F1_PWM1",		BIT_LPSS1_F1_PWM1},
+	{"2  - LPSS1_F2_PWM2",		BIT_LPSS1_F2_PWM2},
+	{"3  - LPSS1_F3_HSUART1",	BIT_LPSS1_F3_HSUART1},
+	{"4  - LPSS1_F4_HSUART2",	BIT_LPSS1_F4_HSUART2},
+	{"5  - LPSS1_F5_SPI",		BIT_LPSS1_F5_SPI},
+	{"6  - LPSS1_F6_Reserved",	BIT_LPSS1_F6_XXX},
+	{"7  - LPSS1_F7_Reserved",	BIT_LPSS1_F7_XXX},
+	{"8  - SCC_EMMC",		BIT_SCC_EMMC},
+	{"9  - SCC_SDIO",		BIT_SCC_SDIO},
+	{"10 - SCC_SDCARD",		BIT_SCC_SDCARD},
+	{"11 - SCC_MIPI",		BIT_SCC_MIPI},
+	{"12 - HDA",			BIT_HDA},
+	{"13 - LPE",			BIT_LPE},
+	{"14 - OTG",			BIT_OTG},
+	{"15 - USH",			BIT_USH},
+	{"16 - GBE",			BIT_GBE},
+	{"17 - SATA",			BIT_SATA},
+	{"18 - USB_EHCI",		BIT_USB_EHCI},
+	{"19 - SEC",			BIT_SEC},
+	{"20 - PCIE_PORT0",		BIT_PCIE_PORT0},
+	{"21 - PCIE_PORT1",		BIT_PCIE_PORT1},
+	{"22 - PCIE_PORT2",		BIT_PCIE_PORT2},
+	{"23 - PCIE_PORT3",		BIT_PCIE_PORT3},
+	{"24 - LPSS2_F0_DMA",		BIT_LPSS2_F0_DMA},
+	{"25 - LPSS2_F1_I2C1",		BIT_LPSS2_F1_I2C1},
+	{"26 - LPSS2_F2_I2C2",		BIT_LPSS2_F2_I2C2},
+	{"27 - LPSS2_F3_I2C3",		BIT_LPSS2_F3_I2C3},
+	{"28 - LPSS2_F3_I2C4",		BIT_LPSS2_F4_I2C4},
+	{"29 - LPSS2_F5_I2C5",		BIT_LPSS2_F5_I2C5},
+	{"30 - LPSS2_F6_I2C6",		BIT_LPSS2_F6_I2C6},
+	{"31 - LPSS2_F7_I2C7",		BIT_LPSS2_F7_I2C7},
+	{"32 - SMB",			BIT_SMB},
+	{"33 - USH_SS_PHY",		BIT_OTG_SS_PHY},
+	{"34 - OTG_SS_PHY",		BIT_USH_SS_PHY},
+	{"35 - DFX",			BIT_DFX},
+};
+
+static inline u32 pmc_reg_read(struct pmc_dev *pmc, int reg_offset)
+{
+	return readl(pmc->regmap + reg_offset);
+}
+
+static void pmc_power_off(void)
+{
+	u16	pm1_cnt_port;
+	u32	pm1_cnt_value;
+
+	printk(KERN_INFO, "PMC: Preparing to enter system sleep state S5\n");
+
+	pm1_cnt_port = acpi_base_addr + PM1_CNT;
+
+	pm1_cnt_value = inl(pm1_cnt_port);
+	pm1_cnt_value &= SLEEP_TYPE_MASK;
+	pm1_cnt_value |= SLEEP_TYPE_S5;
+	pm1_cnt_value |= SLEEP_ENABLE;
+
+	outl(pm1_cnt_value, pm1_cnt_port);
+}
+
+#ifdef CONFIG_DEBUG_FS
+static int pmc_dev_state_show(struct seq_file *s, void *unused)
+{
+	struct pmc_dev *pmc = (struct pmc_dev *)s->private;
+	u32 func_dis, func_dis_2, func_dis_index;
+	u32 d3_sts_0, d3_sts_1, d3_sts_index;
+	int dev_num, dev_index, reg_index;
+
+	func_dis = pmc_reg_read(pmc, PMC_FUNC_DIS);
+	func_dis_2 = pmc_reg_read(pmc, PMC_FUNC_DIS_2);
+	d3_sts_0 = pmc_reg_read(pmc, PMC_D3_STS_0);
+	d3_sts_1 = pmc_reg_read(pmc, PMC_D3_STS_1);
+
+	dev_num = sizeof(dev_map) / sizeof(struct pmc_dev_map);
+
+	for (dev_index = 0; dev_index < dev_num; dev_index++) {
+		reg_index = dev_index / PMC_REG_BIT_WIDTH;
+		if (reg_index) {
+			func_dis_index = func_dis_2;
+			d3_sts_index = d3_sts_1;
+		} else {
+			func_dis_index = func_dis;
+			d3_sts_index = d3_sts_0;
+		}
+
+		if (func_dis_index & dev_map[dev_index].bit_mask) {
+			seq_printf(s, "Dev: %-32s\tState: %s [%s]\n",
+				dev_map[dev_index].name, "Disabled",
+				(d3_sts_index & dev_map[dev_index].bit_mask) ?
+				"D3":"D0");
+		} else {
+			seq_printf(s, "Dev: %-32s\tState: %s [%s]\n",
+				dev_map[dev_index].name, "Enabled ",
+				(d3_sts_index & dev_map[dev_index].bit_mask) ?
+				"D3":"D0");
+		}
+	}
+	return 0;
+}
+
+static int pmc_dev_state_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, pmc_dev_state_show, inode->i_private);
+}
+
+static const struct file_operations pmc_dev_state_ops = {
+	.open		= pmc_dev_state_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int pmc_sleep_tmr_show(struct seq_file *s, void *unused)
+{
+	struct pmc_dev *pmc = (struct pmc_dev *)s->private;
+	u64 s0ir_tmr, s0i1_tmr, s0i2_tmr, s0i3_tmr, s0_tmr;
+
+	s0ir_tmr = pmc_reg_read(pmc, PMC_S0IR_TMR) << PMC_TMR_SHIFT;
+	s0i1_tmr = pmc_reg_read(pmc, PMC_S0I1_TMR) << PMC_TMR_SHIFT;
+	s0i2_tmr = pmc_reg_read(pmc, PMC_S0I2_TMR) << PMC_TMR_SHIFT;
+	s0i3_tmr = pmc_reg_read(pmc, PMC_S0I3_TMR) << PMC_TMR_SHIFT;
+	s0_tmr = pmc_reg_read(pmc, PMC_S0_TMR) << PMC_TMR_SHIFT;
+
+	seq_printf(s, "S0IR Residency:\t%lldus\n", s0ir_tmr);
+	seq_printf(s, "S0I1 Residency:\t%lldus\n", s0i1_tmr);
+	seq_printf(s, "S0I2 Residency:\t%lldus\n", s0i2_tmr);
+	seq_printf(s, "S0I3 Residency:\t%lldus\n", s0i3_tmr);
+	seq_printf(s, "S0   Residency:\t%lldus\n", s0_tmr);
+	return 0;
+}
+
+static int pmc_sleep_tmr_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, pmc_sleep_tmr_show, inode->i_private);
+}
+
+static const struct file_operations pmc_sleep_tmr_ops = {
+	.open		= pmc_sleep_tmr_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static void pmc_dbgfs_unregister(struct pmc_dev *pmc)
+{
+	if (!pmc->dbgfs_dir)
+		return;
+
+	debugfs_remove_recursive(pmc->dbgfs_dir);
+	pmc->dbgfs_dir = NULL;
+}
+
+static int pmc_dbgfs_register(struct pmc_dev *pmc)
+{
+	struct dentry *dir, *f;
+
+	dir = debugfs_create_dir("pmc_atom", NULL);
+	if (!dir)
+		return -ENOMEM;
+
+	f = debugfs_create_file("dev_state", S_IFREG | S_IRUGO,
+				dir, pmc, &pmc_dev_state_ops);
+	if (!f) {
+		dev_err(&pmc->pdev->dev, "dev_states register failed\n");
+		goto err;
+	}
+	f = debugfs_create_file("sleep_state", S_IFREG | S_IRUGO,
+				dir, pmc, &pmc_sleep_tmr_ops);
+	if (!f) {
+		dev_err(&pmc->pdev->dev, "sleep_state register failed\n");
+		goto err;
+	}
+	pmc->dbgfs_dir = dir;
+	return 0;
+err:
+	pmc_dbgfs_unregister(pmc);
+	return -ENODEV;
+}
+#endif /* CONFIG_DEBUG_FS */
+
+static int pmc_probe(struct pci_dev *pdev,
+			  const struct pci_device_id *unused)
+{
+	struct pmc_dev *pmc;
+	int ret;
+
+	ret = pci_enable_device(pdev);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "error: could not enable device\n");
+		goto err_enable_device;
+	}
+
+	ret = pci_request_regions(pdev, DRIVER_NAME);
+	if (ret) {
+		dev_err(&pdev->dev, "error: could not request PCI region\n");
+		goto err_request_regions;
+	}
+
+	pmc = devm_kzalloc(&pdev->dev, sizeof(struct pmc_dev), GFP_KERNEL);
+	if (!pmc) {
+		dev_err(&pdev->dev, "error: could not allocate memory\n");
+		ret = -ENOMEM;
+		goto err_devm_kzalloc;
+	}
+
+	pmc->pdev = pci_dev_get(pdev);
+
+	pci_read_config_dword(pdev, PMC_BASE_ADDR_OFFSET, &pmc->base_addr);
+	pmc->base_addr &= PMC_BASE_ADDR_MASK;
+
+	pmc->regmap = devm_ioremap_nocache(&pdev->dev,
+		pmc->base_addr, PMC_MMIO_REG_LEN);
+	if (!pmc->regmap) {
+		dev_err(&pdev->dev, "error: ioremap failed\n");
+		ret = -ENOMEM;
+		goto err_devm_ioremap;
+	}
+	pci_set_drvdata(pdev, pmc);
+#ifdef CONFIG_DEBUG_FS
+	pmc_dbgfs_register(pmc);
+#endif /* CONFIG_DEBUG_FS */
+
+	/* Install power off function */
+	pci_read_config_dword(pdev, ACPI_BASE_ADDR_OFFSET, &acpi_base_addr);
+	acpi_base_addr &= ACPI_BASE_ADDR_MASK;
+	if (acpi_base_addr != 0 && pm_power_off == NULL)
+		pm_power_off = pmc_power_off;
+	return 0;
+err_devm_ioremap:
+	pci_dev_put(pdev);
+err_devm_kzalloc:
+	pci_release_regions(pdev);
+err_request_regions:
+	pci_disable_device(pdev);
+err_enable_device:
+	dev_err(&pdev->dev, "error: probe failed\n");
+	return ret;
+}
+
+static const struct pci_device_id pmc_pci_ids[] = {
+	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_VLV_PMC) },
+	{ 0, },
+};
+
+MODULE_DEVICE_TABLE(pci, pmc_pci_ids);
+
+static struct pci_driver pmc_pci_driver = {
+	.name		= DRIVER_NAME,
+	.probe		= pmc_probe,
+	.id_table	= pmc_pci_ids,
+};
+
+module_pci_driver(pmc_pci_driver);
+
+MODULE_AUTHOR("Aubrey Li <aubrey.li@linux.intel.com>");
+MODULE_DESCRIPTION("Intel Atom SOC Power Management Controller Interface");
+MODULE_LICENSE("GPL v2");
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH] x86: new Intel Atom SoC power management controller driver
  2014-03-06  3:31 [PATCH] x86: new Intel Atom SoC power management controller driver Li, Aubrey
@ 2014-03-06  3:49 ` H. Peter Anvin
  2014-03-06  5:21   ` Li, Aubrey
  0 siblings, 1 reply; 13+ messages in thread
From: H. Peter Anvin @ 2014-03-06  3:49 UTC (permalink / raw)
  To: Li, Aubrey, Matthew Garrett, mingo, tglx, linux-kernel,
	alan@linux.intel.com

On 03/05/2014 07:31 PM, Li, Aubrey wrote:
> The Power Management Controller (PMC) controls many of the power
> management features present in the SoC. This driver provides
> interface to configure the Power Management Controller (PMC).
> 
> This driver exposes PMC device state and sleep state residency
> via debugfs for observation:
> 	/sys/kernel/debugfs/pmc_atom/dev_state
> 	/sys/kernel/debugfs/pmc_atom/sleep_state
> 
> This driver also provides a native power off function via PMC PCI
> IO port.
> 

What is the reason for putting this in debugfs?

	-hpa



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] x86: new Intel Atom SoC power management controller driver
  2014-03-06  3:49 ` H. Peter Anvin
@ 2014-03-06  5:21   ` Li, Aubrey
  2014-03-06  5:28     ` H. Peter Anvin
  0 siblings, 1 reply; 13+ messages in thread
From: Li, Aubrey @ 2014-03-06  5:21 UTC (permalink / raw)
  To: H. Peter Anvin, Matthew Garrett, mingo, tglx, linux-kernel,
	alan@linux.intel.com

On 2014/3/6 11:49, H. Peter Anvin wrote:
> On 03/05/2014 07:31 PM, Li, Aubrey wrote:
>> The Power Management Controller (PMC) controls many of the power
>> management features present in the SoC. This driver provides
>> interface to configure the Power Management Controller (PMC).
>>
>> This driver exposes PMC device state and sleep state residency
>> via debugfs for observation:
>> 	/sys/kernel/debugfs/pmc_atom/dev_state
>> 	/sys/kernel/debugfs/pmc_atom/sleep_state
>>
>> This driver also provides a native power off function via PMC PCI
>> IO port.
>>
> 
> What is the reason for putting this in debugfs?

S0ix support on Atom SoC needs all of the devices under south complex to
be quiesced, as well as the devices under north complex. This interface
shows all of the devices states under platform control unit. If the
system is not able to enter S0ix, we have a way to diagnose. Or if the
system entered S0ix, we have counters to show S0ix entered and its
residency.

Thanks,
-Aubrey


> 
> 	-hpa
> 
> 
> 
> 


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] x86: new Intel Atom SoC power management controller driver
  2014-03-06  5:21   ` Li, Aubrey
@ 2014-03-06  5:28     ` H. Peter Anvin
  2014-03-06  5:33       ` Li, Aubrey
  0 siblings, 1 reply; 13+ messages in thread
From: H. Peter Anvin @ 2014-03-06  5:28 UTC (permalink / raw)
  To: Li, Aubrey, Matthew Garrett, mingo, tglx, linux-kernel,
	alan@linux.intel.com

On 03/05/2014 09:21 PM, Li, Aubrey wrote:
>>
>> What is the reason for putting this in debugfs?
> 
> S0ix support on Atom SoC needs all of the devices under south complex to
> be quiesced, as well as the devices under north complex. This interface
> shows all of the devices states under platform control unit. If the
> system is not able to enter S0ix, we have a way to diagnose. Or if the
> system entered S0ix, we have counters to show S0ix entered and its
> residency.
> 

I guess the real question is: are you sure this is only for debugging,
or is this going to be an interface that applications or daemons will
actually use (in which case it should live in sysfs).

	-hpa



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] x86: new Intel Atom SoC power management controller driver
  2014-03-06  5:28     ` H. Peter Anvin
@ 2014-03-06  5:33       ` Li, Aubrey
  2014-03-06  5:52         ` H. Peter Anvin
  0 siblings, 1 reply; 13+ messages in thread
From: Li, Aubrey @ 2014-03-06  5:33 UTC (permalink / raw)
  To: H. Peter Anvin, Matthew Garrett, mingo, tglx, linux-kernel,
	alan@linux.intel.com

On 2014/3/6 13:28, H. Peter Anvin wrote:
> On 03/05/2014 09:21 PM, Li, Aubrey wrote:
>>>
>>> What is the reason for putting this in debugfs?
>>
>> S0ix support on Atom SoC needs all of the devices under south complex to
>> be quiesced, as well as the devices under north complex. This interface
>> shows all of the devices states under platform control unit. If the
>> system is not able to enter S0ix, we have a way to diagnose. Or if the
>> system entered S0ix, we have counters to show S0ix entered and its
>> residency.
>>
> 
> I guess the real question is: are you sure this is only for debugging,
> or is this going to be an interface that applications or daemons will
> actually use (in which case it should live in sysfs).

These interfaces are uncommitted and don't have consumers now, I'd like
to keep them unstable in debugfs.

We can change it if we received a requirement to use them later.

Thanks,
-Aubrey

> 
> 	-hpa
> 
> 
> 
> 


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] x86: new Intel Atom SoC power management controller driver
  2014-03-06  5:33       ` Li, Aubrey
@ 2014-03-06  5:52         ` H. Peter Anvin
  2014-03-06  5:58           ` Li, Aubrey
  0 siblings, 1 reply; 13+ messages in thread
From: H. Peter Anvin @ 2014-03-06  5:52 UTC (permalink / raw)
  To: Li, Aubrey, Matthew Garrett, mingo, tglx, linux-kernel,
	alan@linux.intel.com

On 03/05/2014 09:33 PM, Li, Aubrey wrote:
> 
> These interfaces are uncommitted and don't have consumers now, I'd like
> to keep them unstable in debugfs.
> 
> We can change it if we received a requirement to use them later.
> 

What we absolutely don't want to happen is someone starting to use these
unstable interfaces for something other than debugging.  If debugfs ever
needs to be mounted in a system for full functionality, that is a bug.

	-hpa



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] x86: new Intel Atom SoC power management controller driver
  2014-03-06  5:52         ` H. Peter Anvin
@ 2014-03-06  5:58           ` Li, Aubrey
  2014-03-06 16:30             ` H. Peter Anvin
  0 siblings, 1 reply; 13+ messages in thread
From: Li, Aubrey @ 2014-03-06  5:58 UTC (permalink / raw)
  To: H. Peter Anvin, Matthew Garrett, mingo, tglx, linux-kernel,
	alan@linux.intel.com

On 2014/3/6 13:52, H. Peter Anvin wrote:
> On 03/05/2014 09:33 PM, Li, Aubrey wrote:
>>
>> These interfaces are uncommitted and don't have consumers now, I'd like
>> to keep them unstable in debugfs.
>>
>> We can change it if we received a requirement to use them later.
>>
> 
> What we absolutely don't want to happen is someone starting to use these
> unstable interfaces for something other than debugging.  If debugfs ever
> needs to be mounted in a system for full functionality, that is a bug.
> 

Understood. But we don't have a plan to use it now. Just for observation
and diagnosis purpose only.

Are you suggesting we should put them into sysfs now?

Thanks,
-Aubrey

> 	-hpa
> 
> 
> 
> 


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] x86: new Intel Atom SoC power management controller driver
  2014-03-06  5:58           ` Li, Aubrey
@ 2014-03-06 16:30             ` H. Peter Anvin
  2014-03-07  0:26               ` Li, Aubrey
  0 siblings, 1 reply; 13+ messages in thread
From: H. Peter Anvin @ 2014-03-06 16:30 UTC (permalink / raw)
  To: Li, Aubrey, Matthew Garrett, mingo, tglx, linux-kernel,
	alan@linux.intel.com

On 03/05/2014 09:58 PM, Li, Aubrey wrote:
>>
>> What we absolutely don't want to happen is someone starting to use these
>> unstable interfaces for something other than debugging.  If debugfs ever
>> needs to be mounted in a system for full functionality, that is a bug.
> 
> Understood. But we don't have a plan to use it now. Just for observation
> and diagnosis purpose only.
> 
> Are you suggesting we should put them into sysfs now?
> 

No, I'm just trying to understand if there is a risk.

	-hpa



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] x86: new Intel Atom SoC power management controller driver
  2014-03-06 16:30             ` H. Peter Anvin
@ 2014-03-07  0:26               ` Li, Aubrey
  2014-03-07  0:41                 ` Joe Perches
  0 siblings, 1 reply; 13+ messages in thread
From: Li, Aubrey @ 2014-03-07  0:26 UTC (permalink / raw)
  To: H. Peter Anvin, Matthew Garrett, mingo, tglx, linux-kernel,
	alan@linux.intel.com, Joe Perches

On 2014/3/7 0:30, H. Peter Anvin wrote:
> On 03/05/2014 09:58 PM, Li, Aubrey wrote:
>>>
>>> What we absolutely don't want to happen is someone starting to use these
>>> unstable interfaces for something other than debugging.  If debugfs ever
>>> needs to be mounted in a system for full functionality, that is a bug.
>>
>> Understood. But we don't have a plan to use it now. Just for observation
>> and diagnosis purpose only.
>>
>> Are you suggesting we should put them into sysfs now?
>>
> 
> No, I'm just trying to understand if there is a risk.
> 
> 	-hpa
> 
Joe reminded me that my patch is word wrapped while I was not aware, hope this one work.

Thanks,
-Aubrey

>From 6fb474e125ecbab404147d2ca75d6965a4716b53 Mon Sep 17 00:00:00 2001
From: Aubrey Li <aubrey.li@intel.com>
Date: Wed, 5 Mar 2014 21:15:56 -0600
Subject: [PATCH] X86 platform: New Intel Atom SOC power management controller
 driver

The Power Management Controller (PMC) controls many of the power
management features present in the SoC. This driver provides
interface to configure the Power Management Controller (PMC).

This driver exposes PMC device state and sleep state residency
via debugfs:
	/sys/kernel/debugfs/pmc_atom/dev_state
	/sys/kernel/debugfs/pmc_atom/sleep_state

This driver also provides a native power off function via PMC PCI
IO port.

Signed-off-by: Aubrey Li <aubrey.li@intel.com>
---
 arch/x86/Kconfig                |    4 +
 arch/x86/include/asm/pmc_atom.h |   91 ++++++++++++
 arch/x86/kernel/Makefile        |    1 +
 arch/x86/kernel/pmc_atom.c      |  301 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 397 insertions(+)
 create mode 100644 arch/x86/include/asm/pmc_atom.h
 create mode 100644 arch/x86/kernel/pmc_atom.c

diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 0af5250..18f7d38 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -2420,6 +2420,10 @@ config IOSF_MBI
 	  Fabric (IOSF) Sideband MailBox Interface (MBI). For MBI platforms
 	  enumerable by PCI.
 
+config PMC_ATOM
+	def_bool y
+        depends on PCI
+
 source "net/Kconfig"
 
 source "drivers/Kconfig"
diff --git a/arch/x86/include/asm/pmc_atom.h b/arch/x86/include/asm/pmc_atom.h
new file mode 100644
index 0000000..6802d17
--- /dev/null
+++ b/arch/x86/include/asm/pmc_atom.h
@@ -0,0 +1,91 @@
+/*
+ * Intel Atom SOC Power Management Controller Header File
+ * Copyright (c) 2014, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ */
+
+#ifndef PMC_ATOM_H
+#define PMC_ATOM_H
+
+/* ValleyView Power Control Unit PCI Device ID */
+#define	PCI_DEVICE_ID_VLV_PMC	0x0F1C
+
+/* PMC Memory mapped IO registers */
+#define	PMC_BASE_ADDR_OFFSET	0x44
+#define	PMC_BASE_ADDR_MASK	0xFFFFFE00
+#define	PMC_MMIO_REG_LEN	0x100
+#define	PMC_REG_BIT_WIDTH	32
+
+/* BIOS uses FUNC_DIS to disable specific function */
+#define	PMC_FUNC_DIS		0x34
+#define	PMC_FUNC_DIS_2		0x38
+/* The timers acumulate time spent in sleep state */
+#define	PMC_S0IR_TMR		0x80
+#define	PMC_S0I1_TMR		0x84
+#define	PMC_S0I2_TMR		0x88
+#define	PMC_S0I3_TMR		0x8C
+#define	PMC_S0_TMR		0x90
+/* Sleep state counter is in units of of 32us */
+#define	PMC_TMR_SHIFT		5
+
+/* These registers reflect D3 status of functions */
+#define	PMC_D3_STS_0		0xA0
+
+#define	BIT_LPSS1_F0_DMA	(1 << 0)
+#define	BIT_LPSS1_F1_PWM1	(1 << 1)
+#define	BIT_LPSS1_F2_PWM2	(1 << 2)
+#define	BIT_LPSS1_F3_HSUART1	(1 << 3)
+#define	BIT_LPSS1_F4_HSUART2	(1 << 4)
+#define	BIT_LPSS1_F5_SPI	(1 << 5)
+#define	BIT_LPSS1_F6_XXX	(1 << 6)
+#define	BIT_LPSS1_F7_XXX	(1 << 7)
+#define	BIT_SCC_EMMC		(1 << 8)
+#define	BIT_SCC_SDIO		(1 << 9)
+#define	BIT_SCC_SDCARD		(1 << 10)
+#define	BIT_SCC_MIPI		(1 << 11)
+#define	BIT_HDA			(1 << 12)
+#define	BIT_LPE			(1 << 13)
+#define	BIT_OTG			(1 << 14)
+#define	BIT_USH			(1 << 15)
+#define	BIT_GBE			(1 << 16)
+#define	BIT_SATA		(1 << 17)
+#define	BIT_USB_EHCI		(1 << 18)
+#define	BIT_SEC			(1 << 19)
+#define	BIT_PCIE_PORT0		(1 << 20)
+#define	BIT_PCIE_PORT1		(1 << 21)
+#define	BIT_PCIE_PORT2		(1 << 22)
+#define	BIT_PCIE_PORT3		(1 << 23)
+#define	BIT_LPSS2_F0_DMA	(1 << 24)
+#define	BIT_LPSS2_F1_I2C1	(1 << 25)
+#define	BIT_LPSS2_F2_I2C2	(1 << 26)
+#define	BIT_LPSS2_F3_I2C3	(1 << 27)
+#define	BIT_LPSS2_F4_I2C4	(1 << 28)
+#define	BIT_LPSS2_F5_I2C5	(1 << 29)
+#define	BIT_LPSS2_F6_I2C6	(1 << 30)
+#define	BIT_LPSS2_F7_I2C7	(1 << 31)
+
+#define	PMC_D3_STS_1		0xA4
+#define	BIT_SMB			(1 << 0)
+#define	BIT_USH_SS_PHY		(1 << 1)
+#define	BIT_OTG_SS_PHY		(1 << 2)
+#define	BIT_DFX			(1 << 3)
+
+/* PMC I/O Registers */
+#define	ACPI_BASE_ADDR_OFFSET	0x40
+#define	ACPI_BASE_ADDR_MASK	0xFFFFFE00
+#define	ACPI_MMIO_REG_LEN	0x100
+
+#define	PM1_CNT			0x4
+#define	SLEEP_TYPE_MASK		0xFFFFECFF
+#define	SLEEP_TYPE_S5		0x1C00
+#define	SLEEP_ENABLE		0x2000
+#endif /* PMC_ATOM_H */
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
index cb648c8..b71a61a 100644
--- a/arch/x86/kernel/Makefile
+++ b/arch/x86/kernel/Makefile
@@ -104,6 +104,7 @@ obj-$(CONFIG_EFI)			+= sysfb_efi.o
 obj-$(CONFIG_PERF_EVENTS)		+= perf_regs.o
 obj-$(CONFIG_TRACING)			+= tracepoint.o
 obj-$(CONFIG_IOSF_MBI)			+= iosf_mbi.o
+obj-$(CONFIG_PMC_ATOM)			+= pmc_atom.o
 
 ###
 # 64 bit specific files
diff --git a/arch/x86/kernel/pmc_atom.c b/arch/x86/kernel/pmc_atom.c
new file mode 100644
index 0000000..46ece98
--- /dev/null
+++ b/arch/x86/kernel/pmc_atom.c
@@ -0,0 +1,301 @@
+/*
+ * Intel Atom SOC Power Management Controller Driver
+ * Copyright (c) 2014, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/device.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/io.h>
+
+#include <asm/pmc_atom.h>
+
+#define	DRIVER_NAME "pmc_atom"
+
+struct pmc_dev {
+	struct pci_dev *pdev;
+	u32 base_addr;
+	void __iomem *regmap;
+#ifdef CONFIG_DEBUG_FS
+	struct dentry *dbgfs_dir;
+#endif /* CONFIG_DEBUG_FS */
+};
+
+static u32 acpi_base_addr;
+
+struct pmc_dev_map {
+	char *name;
+	u32 bit_mask;
+};
+
+static struct pmc_dev_map dev_map[] = {
+	{"0  - LPSS1_F0_DMA",		BIT_LPSS1_F0_DMA},
+	{"1  - LPSS1_F1_PWM1",		BIT_LPSS1_F1_PWM1},
+	{"2  - LPSS1_F2_PWM2",		BIT_LPSS1_F2_PWM2},
+	{"3  - LPSS1_F3_HSUART1",	BIT_LPSS1_F3_HSUART1},
+	{"4  - LPSS1_F4_HSUART2",	BIT_LPSS1_F4_HSUART2},
+	{"5  - LPSS1_F5_SPI",		BIT_LPSS1_F5_SPI},
+	{"6  - LPSS1_F6_Reserved",	BIT_LPSS1_F6_XXX},
+	{"7  - LPSS1_F7_Reserved",	BIT_LPSS1_F7_XXX},
+	{"8  - SCC_EMMC",		BIT_SCC_EMMC},
+	{"9  - SCC_SDIO",		BIT_SCC_SDIO},
+	{"10 - SCC_SDCARD",		BIT_SCC_SDCARD},
+	{"11 - SCC_MIPI",		BIT_SCC_MIPI},
+	{"12 - HDA",			BIT_HDA},
+	{"13 - LPE",			BIT_LPE},
+	{"14 - OTG",			BIT_OTG},
+	{"15 - USH",			BIT_USH},
+	{"16 - GBE",			BIT_GBE},
+	{"17 - SATA",			BIT_SATA},
+	{"18 - USB_EHCI",		BIT_USB_EHCI},
+	{"19 - SEC",			BIT_SEC},
+	{"20 - PCIE_PORT0",		BIT_PCIE_PORT0},
+	{"21 - PCIE_PORT1",		BIT_PCIE_PORT1},
+	{"22 - PCIE_PORT2",		BIT_PCIE_PORT2},
+	{"23 - PCIE_PORT3",		BIT_PCIE_PORT3},
+	{"24 - LPSS2_F0_DMA",		BIT_LPSS2_F0_DMA},
+	{"25 - LPSS2_F1_I2C1",		BIT_LPSS2_F1_I2C1},
+	{"26 - LPSS2_F2_I2C2",		BIT_LPSS2_F2_I2C2},
+	{"27 - LPSS2_F3_I2C3",		BIT_LPSS2_F3_I2C3},
+	{"28 - LPSS2_F3_I2C4",		BIT_LPSS2_F4_I2C4},
+	{"29 - LPSS2_F5_I2C5",		BIT_LPSS2_F5_I2C5},
+	{"30 - LPSS2_F6_I2C6",		BIT_LPSS2_F6_I2C6},
+	{"31 - LPSS2_F7_I2C7",		BIT_LPSS2_F7_I2C7},
+	{"32 - SMB",			BIT_SMB},
+	{"33 - USH_SS_PHY",		BIT_OTG_SS_PHY},
+	{"34 - OTG_SS_PHY",		BIT_USH_SS_PHY},
+	{"35 - DFX",			BIT_DFX},
+};
+
+static inline u32 pmc_reg_read(struct pmc_dev *pmc, int reg_offset)
+{
+	return readl(pmc->regmap + reg_offset);
+}
+
+static void pmc_power_off(void)
+{
+	u16	pm1_cnt_port;
+	u32	pm1_cnt_value;
+
+	printk(KERN_INFO, "PMC: Preparing to enter system sleep state S5\n");
+
+	pm1_cnt_port = acpi_base_addr + PM1_CNT;
+
+	pm1_cnt_value = inl(pm1_cnt_port);
+	pm1_cnt_value &= SLEEP_TYPE_MASK;
+	pm1_cnt_value |= SLEEP_TYPE_S5;
+	pm1_cnt_value |= SLEEP_ENABLE;
+
+	outl(pm1_cnt_value, pm1_cnt_port);
+}
+
+#ifdef CONFIG_DEBUG_FS
+static int pmc_dev_state_show(struct seq_file *s, void *unused)
+{
+	struct pmc_dev *pmc = (struct pmc_dev *)s->private;
+	u32 func_dis, func_dis_2, func_dis_index;
+	u32 d3_sts_0, d3_sts_1, d3_sts_index;
+	int dev_num, dev_index, reg_index;
+
+	func_dis = pmc_reg_read(pmc, PMC_FUNC_DIS);
+	func_dis_2 = pmc_reg_read(pmc, PMC_FUNC_DIS_2);
+	d3_sts_0 = pmc_reg_read(pmc, PMC_D3_STS_0);
+	d3_sts_1 = pmc_reg_read(pmc, PMC_D3_STS_1);
+
+	dev_num = sizeof(dev_map) / sizeof(struct pmc_dev_map);
+
+	for (dev_index = 0; dev_index < dev_num; dev_index++) {
+		reg_index = dev_index / PMC_REG_BIT_WIDTH;
+		if (reg_index) {
+			func_dis_index = func_dis_2;
+			d3_sts_index = d3_sts_1;
+		} else {
+			func_dis_index = func_dis;
+			d3_sts_index = d3_sts_0;
+		}
+
+		if (func_dis_index & dev_map[dev_index].bit_mask) {
+			seq_printf(s, "Dev: %-32s\tState: %s [%s]\n",
+				dev_map[dev_index].name, "Disabled",
+				(d3_sts_index & dev_map[dev_index].bit_mask) ?
+				"D3":"D0");
+		} else {
+			seq_printf(s, "Dev: %-32s\tState: %s [%s]\n",
+				dev_map[dev_index].name, "Enabled ",
+				(d3_sts_index & dev_map[dev_index].bit_mask) ?
+				"D3":"D0");
+		}
+	}
+	return 0;
+}
+
+static int pmc_dev_state_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, pmc_dev_state_show, inode->i_private);
+}
+
+static const struct file_operations pmc_dev_state_ops = {
+	.open		= pmc_dev_state_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int pmc_sleep_tmr_show(struct seq_file *s, void *unused)
+{
+	struct pmc_dev *pmc = (struct pmc_dev *)s->private;
+	u64 s0ir_tmr, s0i1_tmr, s0i2_tmr, s0i3_tmr, s0_tmr;
+
+	s0ir_tmr = pmc_reg_read(pmc, PMC_S0IR_TMR) << PMC_TMR_SHIFT;
+	s0i1_tmr = pmc_reg_read(pmc, PMC_S0I1_TMR) << PMC_TMR_SHIFT;
+	s0i2_tmr = pmc_reg_read(pmc, PMC_S0I2_TMR) << PMC_TMR_SHIFT;
+	s0i3_tmr = pmc_reg_read(pmc, PMC_S0I3_TMR) << PMC_TMR_SHIFT;
+	s0_tmr = pmc_reg_read(pmc, PMC_S0_TMR) << PMC_TMR_SHIFT;
+
+	seq_printf(s, "S0IR Residency:\t%lldus\n", s0ir_tmr);
+	seq_printf(s, "S0I1 Residency:\t%lldus\n", s0i1_tmr);
+	seq_printf(s, "S0I2 Residency:\t%lldus\n", s0i2_tmr);
+	seq_printf(s, "S0I3 Residency:\t%lldus\n", s0i3_tmr);
+	seq_printf(s, "S0   Residency:\t%lldus\n", s0_tmr);
+	return 0;
+}
+
+static int pmc_sleep_tmr_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, pmc_sleep_tmr_show, inode->i_private);
+}
+
+static const struct file_operations pmc_sleep_tmr_ops = {
+	.open		= pmc_sleep_tmr_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static void pmc_dbgfs_unregister(struct pmc_dev *pmc)
+{
+	if (!pmc->dbgfs_dir)
+		return;
+
+	debugfs_remove_recursive(pmc->dbgfs_dir);
+	pmc->dbgfs_dir = NULL;
+}
+
+static int pmc_dbgfs_register(struct pmc_dev *pmc)
+{
+	struct dentry *dir, *f;
+
+	dir = debugfs_create_dir("pmc_atom", NULL);
+	if (!dir)
+		return -ENOMEM;
+
+	f = debugfs_create_file("dev_state", S_IFREG | S_IRUGO,
+				dir, pmc, &pmc_dev_state_ops);
+	if (!f) {
+		dev_err(&pmc->pdev->dev, "dev_states register failed\n");
+		goto err;
+	}
+	f = debugfs_create_file("sleep_state", S_IFREG | S_IRUGO,
+				dir, pmc, &pmc_sleep_tmr_ops);
+	if (!f) {
+		dev_err(&pmc->pdev->dev, "sleep_state register failed\n");
+		goto err;
+	}
+	pmc->dbgfs_dir = dir;
+	return 0;
+err:
+	pmc_dbgfs_unregister(pmc);
+	return -ENODEV;
+}
+#endif /* CONFIG_DEBUG_FS */
+
+static int pmc_probe(struct pci_dev *pdev,
+			  const struct pci_device_id *unused)
+{
+	struct pmc_dev *pmc;
+	int ret;
+
+	ret = pci_enable_device(pdev);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "error: could not enable device\n");
+		goto err_enable_device;
+	}
+
+	ret = pci_request_regions(pdev, DRIVER_NAME);
+	if (ret) {
+		dev_err(&pdev->dev, "error: could not request PCI region\n");
+		goto err_request_regions;
+	}
+
+	pmc = devm_kzalloc(&pdev->dev, sizeof(struct pmc_dev), GFP_KERNEL);
+	if (!pmc) {
+		dev_err(&pdev->dev, "error: could not allocate memory\n");
+		ret = -ENOMEM;
+		goto err_devm_kzalloc;
+	}
+
+	pmc->pdev = pci_dev_get(pdev);
+
+	pci_read_config_dword(pdev, PMC_BASE_ADDR_OFFSET, &pmc->base_addr);
+	pmc->base_addr &= PMC_BASE_ADDR_MASK;
+
+	pmc->regmap = devm_ioremap_nocache(&pdev->dev,
+		pmc->base_addr, PMC_MMIO_REG_LEN);
+	if (!pmc->regmap) {
+		dev_err(&pdev->dev, "error: ioremap failed\n");
+		ret = -ENOMEM;
+		goto err_devm_ioremap;
+	}
+	pci_set_drvdata(pdev, pmc);
+#ifdef CONFIG_DEBUG_FS
+	pmc_dbgfs_register(pmc);
+#endif /* CONFIG_DEBUG_FS */
+
+	/* Install power off function */
+	pci_read_config_dword(pdev, ACPI_BASE_ADDR_OFFSET, &acpi_base_addr);
+	acpi_base_addr &= ACPI_BASE_ADDR_MASK;
+	if (acpi_base_addr != 0 && pm_power_off == NULL)
+		pm_power_off = pmc_power_off;
+	return 0;
+err_devm_ioremap:
+	pci_dev_put(pdev);
+err_devm_kzalloc:
+	pci_release_regions(pdev);
+err_request_regions:
+	pci_disable_device(pdev);
+err_enable_device:
+	dev_err(&pdev->dev, "error: probe failed\n");
+	return ret;
+}
+
+static const struct pci_device_id pmc_pci_ids[] = {
+	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_VLV_PMC) },
+	{ 0, },
+};
+
+MODULE_DEVICE_TABLE(pci, pmc_pci_ids);
+
+static struct pci_driver pmc_pci_driver = {
+	.name		= DRIVER_NAME,
+	.probe		= pmc_probe,
+	.id_table	= pmc_pci_ids,
+};
+
+module_pci_driver(pmc_pci_driver);
+
+MODULE_AUTHOR("Aubrey Li <aubrey.li@linux.intel.com>");
+MODULE_DESCRIPTION("Intel Atom SOC Power Management Controller Interface");
+MODULE_LICENSE("GPL v2");
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH] x86: new Intel Atom SoC power management controller driver
  2014-03-07  0:26               ` Li, Aubrey
@ 2014-03-07  0:41                 ` Joe Perches
  2014-03-07  2:08                   ` Li, Aubrey
  0 siblings, 1 reply; 13+ messages in thread
From: Joe Perches @ 2014-03-07  0:41 UTC (permalink / raw)
  To: Li, Aubrey
  Cc: H. Peter Anvin, Matthew Garrett, mingo, tglx, linux-kernel,
	alan@linux.intel.com

On Fri, 2014-03-07 at 08:26 +0800, Li, Aubrey wrote:

> my patch is word wrapped while I was not aware, hope this one work.

trivial notes:

> diff --git a/arch/x86/include/asm/pmc_atom.h b/arch/x86/include/asm/pmc_atom.h
[]
> +#define	BIT_LPSS1_F0_DMA	(1 << 0)
> +#define	BIT_LPSS1_F1_PWM1	(1 << 1)

These could use BIT(foo)

#define	BIT_LPSS1_F0_DMA	BIT(0)
#define	BIT_LPSS1_F1_PWM1	BIT(1)

etc..

> +#define	BIT_SMB			(1 << 0)
> +#define	BIT_USH_SS_PHY		(1 << 1)
> +#define	BIT_OTG_SS_PHY		(1 << 2)
> +#define	BIT_DFX			(1 << 3)

here too.

> diff --git a/arch/x86/kernel/pmc_atom.c b/arch/x86/kernel/pmc_atom.c
[]
> @@ -0,0 +1,301 @@
> +/*
> + * Intel Atom SOC Power Management Controller Driver
[]
> + */

#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

> +static void pmc_power_off(void)
> +{
[]
> +	printk(KERN_INFO, "PMC: Preparing to enter system sleep state S5\n");

	pr_info("Preparing to enter system sleep state S5\n");

> +		if (func_dis_index & dev_map[dev_index].bit_mask) {
> +			seq_printf(s, "Dev: %-32s\tState: %s [%s]\n",
> +				dev_map[dev_index].name, "Disabled",
> +				(d3_sts_index & dev_map[dev_index].bit_mask) ?
> +				"D3":"D0");
> +		} else {
> +			seq_printf(s, "Dev: %-32s\tState: %s [%s]\n",
> +				dev_map[dev_index].name, "Enabled ",
> +				(d3_sts_index & dev_map[dev_index].bit_mask) ?
> +				"D3":"D0");
> +		}

Maybe better with a single seq_printf

		seq_printf(s, "Dev: %-32s\tState: %s [%s]\n",
			   dev_map[dev_index].name,
			   dev_map[dev_index].bit_mask & func_dis_index ?
			   "Disabled" : " Enabled ",
			   dev_map[dev_index].bit_mask & d3_sts_index ?
			   "D3" : "D0");

> +static int pmc_probe(struct pci_dev *pdev,
> +			  const struct pci_device_id *unused)
> +{

> +	pmc = devm_kzalloc(&pdev->dev, sizeof(struct pmc_dev), GFP_KERNEL);
> +	if (!pmc) {
> +		dev_err(&pdev->dev, "error: could not allocate memory\n");

Unnecessary OOM message, kzalloc will dump_stack()



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] x86: new Intel Atom SoC power management controller driver
  2014-03-07  0:41                 ` Joe Perches
@ 2014-03-07  2:08                   ` Li, Aubrey
  2014-03-07  2:21                     ` Joe Perches
  0 siblings, 1 reply; 13+ messages in thread
From: Li, Aubrey @ 2014-03-07  2:08 UTC (permalink / raw)
  To: Joe Perches
  Cc: H. Peter Anvin, Matthew Garrett, mingo, tglx, linux-kernel,
	alan@linux.intel.com

On 2014/3/7 8:41, Joe Perches wrote:
> On Fri, 2014-03-07 at 08:26 +0800, Li, Aubrey wrote:
> 
>> my patch is word wrapped while I was not aware, hope this one work.
> 
> trivial notes:
> 
Thanks for your comments, Joe. Patch refined as below:

-Aubrey

[PATCH] X86 platform: New Intel Atom SOC power management controller driver

The Power Management Controller (PMC) controls many of the power
management features present in the SoC. This driver provides
interface to configure the Power Management Controller (PMC).

This driver exposes PMC device state and sleep state residency
via debugfs:
	/sys/kernel/debugfs/pmc_atom/dev_state
	/sys/kernel/debugfs/pmc_atom/sleep_state

This driver also provides a native power off function via PMC PCI
IO port.

Signed-off-by: Aubrey Li <aubrey.li@intel.com>
Reviewed-by: Joe Perches <joe@perches.com>
---
 arch/x86/Kconfig                |    4 +
 arch/x86/include/asm/pmc_atom.h |   91 ++++++++++++
 arch/x86/kernel/Makefile        |    1 +
 arch/x86/kernel/pmc_atom.c      |  295 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 391 insertions(+)
 create mode 100644 arch/x86/include/asm/pmc_atom.h
 create mode 100644 arch/x86/kernel/pmc_atom.c

diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 0af5250..18f7d38 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -2420,6 +2420,10 @@ config IOSF_MBI
 	  Fabric (IOSF) Sideband MailBox Interface (MBI). For MBI platforms
 	  enumerable by PCI.
 
+config PMC_ATOM
+	def_bool y
+        depends on PCI
+
 source "net/Kconfig"
 
 source "drivers/Kconfig"
diff --git a/arch/x86/include/asm/pmc_atom.h b/arch/x86/include/asm/pmc_atom.h
new file mode 100644
index 0000000..43b68fc
--- /dev/null
+++ b/arch/x86/include/asm/pmc_atom.h
@@ -0,0 +1,91 @@
+/*
+ * Intel Atom SOC Power Management Controller Header File
+ * Copyright (c) 2014, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ */
+
+#ifndef PMC_ATOM_H
+#define PMC_ATOM_H
+
+/* ValleyView Power Control Unit PCI Device ID */
+#define	PCI_DEVICE_ID_VLV_PMC	0x0F1C
+
+/* PMC Memory mapped IO registers */
+#define	PMC_BASE_ADDR_OFFSET	0x44
+#define	PMC_BASE_ADDR_MASK	0xFFFFFE00
+#define	PMC_MMIO_REG_LEN	0x100
+#define	PMC_REG_BIT_WIDTH	32
+
+/* BIOS uses FUNC_DIS to disable specific function */
+#define	PMC_FUNC_DIS		0x34
+#define	PMC_FUNC_DIS_2		0x38
+/* The timers acumulate time spent in sleep state */
+#define	PMC_S0IR_TMR		0x80
+#define	PMC_S0I1_TMR		0x84
+#define	PMC_S0I2_TMR		0x88
+#define	PMC_S0I3_TMR		0x8C
+#define	PMC_S0_TMR		0x90
+/* Sleep state counter is in units of of 32us */
+#define	PMC_TMR_SHIFT		5
+
+/* These registers reflect D3 status of functions */
+#define	PMC_D3_STS_0		0xA0
+
+#define	BIT_LPSS1_F0_DMA	BIT(0)
+#define	BIT_LPSS1_F1_PWM1	BIT(1)
+#define	BIT_LPSS1_F2_PWM2	BIT(2)
+#define	BIT_LPSS1_F3_HSUART1	BIT(3)
+#define	BIT_LPSS1_F4_HSUART2	BIT(4)
+#define	BIT_LPSS1_F5_SPI	BIT(5)
+#define	BIT_LPSS1_F6_XXX	BIT(6)
+#define	BIT_LPSS1_F7_XXX	BIT(7)
+#define	BIT_SCC_EMMC		BIT(8)
+#define	BIT_SCC_SDIO		BIT(9)
+#define	BIT_SCC_SDCARD		BIT(10)
+#define	BIT_SCC_MIPI		BIT(11)
+#define	BIT_HDA			BIT(12)
+#define	BIT_LPE			BIT(13)
+#define	BIT_OTG			BIT(14)
+#define	BIT_USH			BIT(15)
+#define	BIT_GBE			BIT(16)
+#define	BIT_SATA		BIT(17)
+#define	BIT_USB_EHCI		BIT(18)
+#define	BIT_SEC			BIT(19)
+#define	BIT_PCIE_PORT0		BIT(20)
+#define	BIT_PCIE_PORT1		BIT(21)
+#define	BIT_PCIE_PORT2		BIT(22)
+#define	BIT_PCIE_PORT3		BIT(23)
+#define	BIT_LPSS2_F0_DMA	BIT(24)
+#define	BIT_LPSS2_F1_I2C1	BIT(25)
+#define	BIT_LPSS2_F2_I2C2	BIT(26)
+#define	BIT_LPSS2_F3_I2C3	BIT(27)
+#define	BIT_LPSS2_F4_I2C4	BIT(28)
+#define	BIT_LPSS2_F5_I2C5	BIT(29)
+#define	BIT_LPSS2_F6_I2C6	BIT(30)
+#define	BIT_LPSS2_F7_I2C7	BIT(31)
+
+#define	PMC_D3_STS_1		0xA4
+#define	BIT_SMB			BIT(0)
+#define	BIT_USH_SS_PHY		BIT(1)
+#define	BIT_OTG_SS_PHY		BIT(2)
+#define	BIT_DFX			BIT(3)
+
+/* PMC I/O Registers */
+#define	ACPI_BASE_ADDR_OFFSET	0x40
+#define	ACPI_BASE_ADDR_MASK	0xFFFFFE00
+#define	ACPI_MMIO_REG_LEN	0x100
+
+#define	PM1_CNT			0x4
+#define	SLEEP_TYPE_MASK		0xFFFFECFF
+#define	SLEEP_TYPE_S5		0x1C00
+#define	SLEEP_ENABLE		0x2000
+#endif /* PMC_ATOM_H */
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
index cb648c8..b71a61a 100644
--- a/arch/x86/kernel/Makefile
+++ b/arch/x86/kernel/Makefile
@@ -104,6 +104,7 @@ obj-$(CONFIG_EFI)			+= sysfb_efi.o
 obj-$(CONFIG_PERF_EVENTS)		+= perf_regs.o
 obj-$(CONFIG_TRACING)			+= tracepoint.o
 obj-$(CONFIG_IOSF_MBI)			+= iosf_mbi.o
+obj-$(CONFIG_PMC_ATOM)			+= pmc_atom.o
 
 ###
 # 64 bit specific files
diff --git a/arch/x86/kernel/pmc_atom.c b/arch/x86/kernel/pmc_atom.c
new file mode 100644
index 0000000..695bbd3
--- /dev/null
+++ b/arch/x86/kernel/pmc_atom.c
@@ -0,0 +1,295 @@
+/*
+ * Intel Atom SOC Power Management Controller Driver
+ * Copyright (c) 2014, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/device.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/io.h>
+
+#include <asm/pmc_atom.h>
+
+#define	DRIVER_NAME "pmc_atom"
+
+struct pmc_dev {
+	struct pci_dev *pdev;
+	u32 base_addr;
+	void __iomem *regmap;
+#ifdef CONFIG_DEBUG_FS
+	struct dentry *dbgfs_dir;
+#endif /* CONFIG_DEBUG_FS */
+};
+
+static u32 acpi_base_addr;
+
+struct pmc_dev_map {
+	char *name;
+	u32 bit_mask;
+};
+
+static struct pmc_dev_map dev_map[] = {
+	{"0  - LPSS1_F0_DMA",		BIT_LPSS1_F0_DMA},
+	{"1  - LPSS1_F1_PWM1",		BIT_LPSS1_F1_PWM1},
+	{"2  - LPSS1_F2_PWM2",		BIT_LPSS1_F2_PWM2},
+	{"3  - LPSS1_F3_HSUART1",	BIT_LPSS1_F3_HSUART1},
+	{"4  - LPSS1_F4_HSUART2",	BIT_LPSS1_F4_HSUART2},
+	{"5  - LPSS1_F5_SPI",		BIT_LPSS1_F5_SPI},
+	{"6  - LPSS1_F6_Reserved",	BIT_LPSS1_F6_XXX},
+	{"7  - LPSS1_F7_Reserved",	BIT_LPSS1_F7_XXX},
+	{"8  - SCC_EMMC",		BIT_SCC_EMMC},
+	{"9  - SCC_SDIO",		BIT_SCC_SDIO},
+	{"10 - SCC_SDCARD",		BIT_SCC_SDCARD},
+	{"11 - SCC_MIPI",		BIT_SCC_MIPI},
+	{"12 - HDA",			BIT_HDA},
+	{"13 - LPE",			BIT_LPE},
+	{"14 - OTG",			BIT_OTG},
+	{"15 - USH",			BIT_USH},
+	{"16 - GBE",			BIT_GBE},
+	{"17 - SATA",			BIT_SATA},
+	{"18 - USB_EHCI",		BIT_USB_EHCI},
+	{"19 - SEC",			BIT_SEC},
+	{"20 - PCIE_PORT0",		BIT_PCIE_PORT0},
+	{"21 - PCIE_PORT1",		BIT_PCIE_PORT1},
+	{"22 - PCIE_PORT2",		BIT_PCIE_PORT2},
+	{"23 - PCIE_PORT3",		BIT_PCIE_PORT3},
+	{"24 - LPSS2_F0_DMA",		BIT_LPSS2_F0_DMA},
+	{"25 - LPSS2_F1_I2C1",		BIT_LPSS2_F1_I2C1},
+	{"26 - LPSS2_F2_I2C2",		BIT_LPSS2_F2_I2C2},
+	{"27 - LPSS2_F3_I2C3",		BIT_LPSS2_F3_I2C3},
+	{"28 - LPSS2_F3_I2C4",		BIT_LPSS2_F4_I2C4},
+	{"29 - LPSS2_F5_I2C5",		BIT_LPSS2_F5_I2C5},
+	{"30 - LPSS2_F6_I2C6",		BIT_LPSS2_F6_I2C6},
+	{"31 - LPSS2_F7_I2C7",		BIT_LPSS2_F7_I2C7},
+	{"32 - SMB",			BIT_SMB},
+	{"33 - USH_SS_PHY",		BIT_OTG_SS_PHY},
+	{"34 - OTG_SS_PHY",		BIT_USH_SS_PHY},
+	{"35 - DFX",			BIT_DFX},
+};
+
+static inline u32 pmc_reg_read(struct pmc_dev *pmc, int reg_offset)
+{
+	return readl(pmc->regmap + reg_offset);
+}
+
+static void pmc_power_off(void)
+{
+	u16	pm1_cnt_port;
+	u32	pm1_cnt_value;
+
+	pr_info("%s: Preparing to enter system sleep state S5\n", DRIVER_NAME);
+
+	pm1_cnt_port = acpi_base_addr + PM1_CNT;
+
+	pm1_cnt_value = inl(pm1_cnt_port);
+	pm1_cnt_value &= SLEEP_TYPE_MASK;
+	pm1_cnt_value |= SLEEP_TYPE_S5;
+	pm1_cnt_value |= SLEEP_ENABLE;
+
+	outl(pm1_cnt_value, pm1_cnt_port);
+}
+
+#ifdef CONFIG_DEBUG_FS
+static int pmc_dev_state_show(struct seq_file *s, void *unused)
+{
+	struct pmc_dev *pmc = (struct pmc_dev *)s->private;
+	u32 func_dis, func_dis_2, func_dis_index;
+	u32 d3_sts_0, d3_sts_1, d3_sts_index;
+	int dev_num, dev_index, reg_index;
+
+	func_dis = pmc_reg_read(pmc, PMC_FUNC_DIS);
+	func_dis_2 = pmc_reg_read(pmc, PMC_FUNC_DIS_2);
+	d3_sts_0 = pmc_reg_read(pmc, PMC_D3_STS_0);
+	d3_sts_1 = pmc_reg_read(pmc, PMC_D3_STS_1);
+
+	dev_num = sizeof(dev_map) / sizeof(struct pmc_dev_map);
+
+	for (dev_index = 0; dev_index < dev_num; dev_index++) {
+		reg_index = dev_index / PMC_REG_BIT_WIDTH;
+		if (reg_index) {
+			func_dis_index = func_dis_2;
+			d3_sts_index = d3_sts_1;
+		} else {
+			func_dis_index = func_dis;
+			d3_sts_index = d3_sts_0;
+		}
+
+		seq_printf(s, "Dev: %-32s\tState: %s [%s]\n",
+			dev_map[dev_index].name,
+			dev_map[dev_index].bit_mask & func_dis_index ?
+			"Disabled" : "Enabled ",
+			(d3_sts_index & dev_map[dev_index].bit_mask) ?
+			"D3" : "D0");
+	}
+	return 0;
+}
+
+static int pmc_dev_state_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, pmc_dev_state_show, inode->i_private);
+}
+
+static const struct file_operations pmc_dev_state_ops = {
+	.open		= pmc_dev_state_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int pmc_sleep_tmr_show(struct seq_file *s, void *unused)
+{
+	struct pmc_dev *pmc = (struct pmc_dev *)s->private;
+	u64 s0ir_tmr, s0i1_tmr, s0i2_tmr, s0i3_tmr, s0_tmr;
+
+	s0ir_tmr = pmc_reg_read(pmc, PMC_S0IR_TMR) << PMC_TMR_SHIFT;
+	s0i1_tmr = pmc_reg_read(pmc, PMC_S0I1_TMR) << PMC_TMR_SHIFT;
+	s0i2_tmr = pmc_reg_read(pmc, PMC_S0I2_TMR) << PMC_TMR_SHIFT;
+	s0i3_tmr = pmc_reg_read(pmc, PMC_S0I3_TMR) << PMC_TMR_SHIFT;
+	s0_tmr = pmc_reg_read(pmc, PMC_S0_TMR) << PMC_TMR_SHIFT;
+
+	seq_printf(s, "S0IR Residency:\t%lldus\n", s0ir_tmr);
+	seq_printf(s, "S0I1 Residency:\t%lldus\n", s0i1_tmr);
+	seq_printf(s, "S0I2 Residency:\t%lldus\n", s0i2_tmr);
+	seq_printf(s, "S0I3 Residency:\t%lldus\n", s0i3_tmr);
+	seq_printf(s, "S0   Residency:\t%lldus\n", s0_tmr);
+	return 0;
+}
+
+static int pmc_sleep_tmr_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, pmc_sleep_tmr_show, inode->i_private);
+}
+
+static const struct file_operations pmc_sleep_tmr_ops = {
+	.open		= pmc_sleep_tmr_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static void pmc_dbgfs_unregister(struct pmc_dev *pmc)
+{
+	if (!pmc->dbgfs_dir)
+		return;
+
+	debugfs_remove_recursive(pmc->dbgfs_dir);
+	pmc->dbgfs_dir = NULL;
+}
+
+static int pmc_dbgfs_register(struct pmc_dev *pmc)
+{
+	struct dentry *dir, *f;
+
+	dir = debugfs_create_dir("pmc_atom", NULL);
+	if (!dir)
+		return -ENOMEM;
+
+	f = debugfs_create_file("dev_state", S_IFREG | S_IRUGO,
+				dir, pmc, &pmc_dev_state_ops);
+	if (!f) {
+		dev_err(&pmc->pdev->dev, "dev_states register failed\n");
+		goto err;
+	}
+	f = debugfs_create_file("sleep_state", S_IFREG | S_IRUGO,
+				dir, pmc, &pmc_sleep_tmr_ops);
+	if (!f) {
+		dev_err(&pmc->pdev->dev, "sleep_state register failed\n");
+		goto err;
+	}
+	pmc->dbgfs_dir = dir;
+	return 0;
+err:
+	pmc_dbgfs_unregister(pmc);
+	return -ENODEV;
+}
+#endif /* CONFIG_DEBUG_FS */
+
+static int pmc_probe(struct pci_dev *pdev,
+			  const struct pci_device_id *unused)
+{
+	struct pmc_dev *pmc;
+	int ret;
+
+	ret = pci_enable_device(pdev);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "error: could not enable device\n");
+		goto err_enable_device;
+	}
+
+	ret = pci_request_regions(pdev, DRIVER_NAME);
+	if (ret) {
+		dev_err(&pdev->dev, "error: could not request PCI region\n");
+		goto err_request_regions;
+	}
+
+	pmc = devm_kzalloc(&pdev->dev, sizeof(struct pmc_dev), GFP_KERNEL);
+	if (!pmc) {
+		ret = -ENOMEM;
+		goto err_devm_kzalloc;
+	}
+
+	pmc->pdev = pci_dev_get(pdev);
+
+	pci_read_config_dword(pdev, PMC_BASE_ADDR_OFFSET, &pmc->base_addr);
+	pmc->base_addr &= PMC_BASE_ADDR_MASK;
+
+	pmc->regmap = devm_ioremap_nocache(&pdev->dev,
+		pmc->base_addr, PMC_MMIO_REG_LEN);
+	if (!pmc->regmap) {
+		dev_err(&pdev->dev, "error: ioremap failed\n");
+		ret = -ENOMEM;
+		goto err_devm_ioremap;
+	}
+	pci_set_drvdata(pdev, pmc);
+#ifdef CONFIG_DEBUG_FS
+	pmc_dbgfs_register(pmc);
+#endif /* CONFIG_DEBUG_FS */
+
+	/* Install power off function */
+	pci_read_config_dword(pdev, ACPI_BASE_ADDR_OFFSET, &acpi_base_addr);
+	acpi_base_addr &= ACPI_BASE_ADDR_MASK;
+	if (acpi_base_addr != 0 && pm_power_off == NULL)
+		pm_power_off = pmc_power_off;
+	return 0;
+err_devm_ioremap:
+	pci_dev_put(pdev);
+err_devm_kzalloc:
+	pci_release_regions(pdev);
+err_request_regions:
+	pci_disable_device(pdev);
+err_enable_device:
+	dev_err(&pdev->dev, "error: probe failed\n");
+	return ret;
+}
+
+static const struct pci_device_id pmc_pci_ids[] = {
+	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_VLV_PMC) },
+	{ 0, },
+};
+
+MODULE_DEVICE_TABLE(pci, pmc_pci_ids);
+
+static struct pci_driver pmc_pci_driver = {
+	.name		= DRIVER_NAME,
+	.probe		= pmc_probe,
+	.id_table	= pmc_pci_ids,
+};
+
+module_pci_driver(pmc_pci_driver);
+
+MODULE_AUTHOR("Aubrey Li <aubrey.li@linux.intel.com>");
+MODULE_DESCRIPTION("Intel Atom SOC Power Management Controller Interface");
+MODULE_LICENSE("GPL v2");
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH] x86: new Intel Atom SoC power management controller driver
  2014-03-07  2:08                   ` Li, Aubrey
@ 2014-03-07  2:21                     ` Joe Perches
  2014-03-07  3:04                       ` Li, Aubrey
  0 siblings, 1 reply; 13+ messages in thread
From: Joe Perches @ 2014-03-07  2:21 UTC (permalink / raw)
  To: Li, Aubrey
  Cc: H. Peter Anvin, Matthew Garrett, mingo, tglx, linux-kernel,
	alan@linux.intel.com

On Fri, 2014-03-07 at 10:08 +0800, Li, Aubrey wrote:

> The Power Management Controller (PMC) controls many of the power
> management features present in the SoC. This driver provides
> interface to configure the Power Management Controller (PMC).

More trivial notes.

Nothing really that should stop this from being applied.

> diff --git a/arch/x86/kernel/pmc_atom.c b/arch/x86/kernel/pmc_atom.c

I still think that
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
is useful here.

> +#include <linux/module.h>
> +#include <linux/init.h>
[]
> +#define	DRIVER_NAME "pmc_atom"

Maybe

#define DRIVER_NAME KBUILD_MODNAME

too

> +struct pmc_dev_map {
> +       char *name;
> +       u32 bit_mask;
> +};
> 
> +static struct pmc_dev_map dev_map[] = {

static const to reduce data?

struct pmc_dev_map {
	const char *name;
	u32 bit_mask;
};

> +static struct pmc_dev_map dev_map[] = {

static const struct pmc_dev_map dev_map[] = {

> +	{"0  - LPSS1_F0_DMA",		BIT_LPSS1_F0_DMA},
> +	{"1  - LPSS1_F1_PWM1",		BIT_LPSS1_F1_PWM1},

[]

> +static void pmc_power_off(void)
> +{
[]
> +	pr_info("%s: Preparing to enter system sleep state S5\n", DRIVER_NAME);

This is larger code than using pr_fmt

> +#ifdef CONFIG_DEBUG_FS
> +static int pmc_dev_state_show(struct seq_file *s, void *unused)
> +{
[]
> +		seq_printf(s, "Dev: %-32s\tState: %s [%s]\n",
> +			dev_map[dev_index].name,
> +			dev_map[dev_index].bit_mask & func_dis_index ?
> +			"Disabled" : "Enabled ",
> +			(d3_sts_index & dev_map[dev_index].bit_mask) ?
> +			"D3" : "D0");

This reverses the order of the 1st and 2nd trigraph test
and to me would be easier to read like:

		seq_printf(s, "Dev: %-32s\tState: %s [%s]\n",
			dev_map[dev_index].name,
			dev_map[dev_index].bit_mask & func_dis_index ?
			"Disabled" : "Enabled ",
			dev_map[dev_index].bit_mask & d3_sts_index ?
			"D3" : "D0");



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] x86: new Intel Atom SoC power management controller driver
  2014-03-07  2:21                     ` Joe Perches
@ 2014-03-07  3:04                       ` Li, Aubrey
  0 siblings, 0 replies; 13+ messages in thread
From: Li, Aubrey @ 2014-03-07  3:04 UTC (permalink / raw)
  To: Joe Perches
  Cc: H. Peter Anvin, Matthew Garrett, mingo, tglx, linux-kernel,
	alan@linux.intel.com

On 2014/3/7 10:21, Joe Perches wrote:
> On Fri, 2014-03-07 at 10:08 +0800, Li, Aubrey wrote:
> 
>> The Power Management Controller (PMC) controls many of the power
>> management features present in the SoC. This driver provides
>> interface to configure the Power Management Controller (PMC).
> 
> More trivial notes.
> 
> Nothing really that should stop this from being applied.
>
All make sense to me. Welcome your more comments, Joe!

Thanks,
-Aubrey

[PATCH] X86 platform: New Intel Atom SOC power management controller driver

The Power Management Controller (PMC) controls many of the power
management features present in the SoC. This driver provides
interface to configure the Power Management Controller (PMC).

This driver exposes PMC device state and sleep state residency
via debugfs:
	/sys/kernel/debugfs/pmc_atom/dev_state
	/sys/kernel/debugfs/pmc_atom/sleep_state

This driver also provides a native power off function via PMC PCI
IO port.

Signed-off-by: Aubrey Li <aubrey.li@intel.com>
Reviewed-by: Joe Perches <joe@perches.com>
---
 arch/x86/Kconfig                |    4 +
 arch/x86/include/asm/pmc_atom.h |   91 ++++++++++++
 arch/x86/kernel/Makefile        |    1 +
 arch/x86/kernel/pmc_atom.c      |  297 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 393 insertions(+)
 create mode 100644 arch/x86/include/asm/pmc_atom.h
 create mode 100644 arch/x86/kernel/pmc_atom.c

diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 0af5250..18f7d38 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -2420,6 +2420,10 @@ config IOSF_MBI
 	  Fabric (IOSF) Sideband MailBox Interface (MBI). For MBI platforms
 	  enumerable by PCI.
 
+config PMC_ATOM
+	def_bool y
+        depends on PCI
+
 source "net/Kconfig"
 
 source "drivers/Kconfig"
diff --git a/arch/x86/include/asm/pmc_atom.h b/arch/x86/include/asm/pmc_atom.h
new file mode 100644
index 0000000..43b68fc
--- /dev/null
+++ b/arch/x86/include/asm/pmc_atom.h
@@ -0,0 +1,91 @@
+/*
+ * Intel Atom SOC Power Management Controller Header File
+ * Copyright (c) 2014, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ */
+
+#ifndef PMC_ATOM_H
+#define PMC_ATOM_H
+
+/* ValleyView Power Control Unit PCI Device ID */
+#define	PCI_DEVICE_ID_VLV_PMC	0x0F1C
+
+/* PMC Memory mapped IO registers */
+#define	PMC_BASE_ADDR_OFFSET	0x44
+#define	PMC_BASE_ADDR_MASK	0xFFFFFE00
+#define	PMC_MMIO_REG_LEN	0x100
+#define	PMC_REG_BIT_WIDTH	32
+
+/* BIOS uses FUNC_DIS to disable specific function */
+#define	PMC_FUNC_DIS		0x34
+#define	PMC_FUNC_DIS_2		0x38
+/* The timers acumulate time spent in sleep state */
+#define	PMC_S0IR_TMR		0x80
+#define	PMC_S0I1_TMR		0x84
+#define	PMC_S0I2_TMR		0x88
+#define	PMC_S0I3_TMR		0x8C
+#define	PMC_S0_TMR		0x90
+/* Sleep state counter is in units of of 32us */
+#define	PMC_TMR_SHIFT		5
+
+/* These registers reflect D3 status of functions */
+#define	PMC_D3_STS_0		0xA0
+
+#define	BIT_LPSS1_F0_DMA	BIT(0)
+#define	BIT_LPSS1_F1_PWM1	BIT(1)
+#define	BIT_LPSS1_F2_PWM2	BIT(2)
+#define	BIT_LPSS1_F3_HSUART1	BIT(3)
+#define	BIT_LPSS1_F4_HSUART2	BIT(4)
+#define	BIT_LPSS1_F5_SPI	BIT(5)
+#define	BIT_LPSS1_F6_XXX	BIT(6)
+#define	BIT_LPSS1_F7_XXX	BIT(7)
+#define	BIT_SCC_EMMC		BIT(8)
+#define	BIT_SCC_SDIO		BIT(9)
+#define	BIT_SCC_SDCARD		BIT(10)
+#define	BIT_SCC_MIPI		BIT(11)
+#define	BIT_HDA			BIT(12)
+#define	BIT_LPE			BIT(13)
+#define	BIT_OTG			BIT(14)
+#define	BIT_USH			BIT(15)
+#define	BIT_GBE			BIT(16)
+#define	BIT_SATA		BIT(17)
+#define	BIT_USB_EHCI		BIT(18)
+#define	BIT_SEC			BIT(19)
+#define	BIT_PCIE_PORT0		BIT(20)
+#define	BIT_PCIE_PORT1		BIT(21)
+#define	BIT_PCIE_PORT2		BIT(22)
+#define	BIT_PCIE_PORT3		BIT(23)
+#define	BIT_LPSS2_F0_DMA	BIT(24)
+#define	BIT_LPSS2_F1_I2C1	BIT(25)
+#define	BIT_LPSS2_F2_I2C2	BIT(26)
+#define	BIT_LPSS2_F3_I2C3	BIT(27)
+#define	BIT_LPSS2_F4_I2C4	BIT(28)
+#define	BIT_LPSS2_F5_I2C5	BIT(29)
+#define	BIT_LPSS2_F6_I2C6	BIT(30)
+#define	BIT_LPSS2_F7_I2C7	BIT(31)
+
+#define	PMC_D3_STS_1		0xA4
+#define	BIT_SMB			BIT(0)
+#define	BIT_USH_SS_PHY		BIT(1)
+#define	BIT_OTG_SS_PHY		BIT(2)
+#define	BIT_DFX			BIT(3)
+
+/* PMC I/O Registers */
+#define	ACPI_BASE_ADDR_OFFSET	0x40
+#define	ACPI_BASE_ADDR_MASK	0xFFFFFE00
+#define	ACPI_MMIO_REG_LEN	0x100
+
+#define	PM1_CNT			0x4
+#define	SLEEP_TYPE_MASK		0xFFFFECFF
+#define	SLEEP_TYPE_S5		0x1C00
+#define	SLEEP_ENABLE		0x2000
+#endif /* PMC_ATOM_H */
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
index cb648c8..b71a61a 100644
--- a/arch/x86/kernel/Makefile
+++ b/arch/x86/kernel/Makefile
@@ -104,6 +104,7 @@ obj-$(CONFIG_EFI)			+= sysfb_efi.o
 obj-$(CONFIG_PERF_EVENTS)		+= perf_regs.o
 obj-$(CONFIG_TRACING)			+= tracepoint.o
 obj-$(CONFIG_IOSF_MBI)			+= iosf_mbi.o
+obj-$(CONFIG_PMC_ATOM)			+= pmc_atom.o
 
 ###
 # 64 bit specific files
diff --git a/arch/x86/kernel/pmc_atom.c b/arch/x86/kernel/pmc_atom.c
new file mode 100644
index 0000000..5991030
--- /dev/null
+++ b/arch/x86/kernel/pmc_atom.c
@@ -0,0 +1,297 @@
+/*
+ * Intel Atom SOC Power Management Controller Driver
+ * Copyright (c) 2014, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/device.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/io.h>
+
+#include <asm/pmc_atom.h>
+
+#define	DRIVER_NAME	KBUILD_MODNAME
+
+struct pmc_dev {
+	struct pci_dev *pdev;
+	u32 base_addr;
+	void __iomem *regmap;
+#ifdef CONFIG_DEBUG_FS
+	struct dentry *dbgfs_dir;
+#endif /* CONFIG_DEBUG_FS */
+};
+
+static u32 acpi_base_addr;
+
+struct pmc_dev_map {
+	const char *name;
+	u32 bit_mask;
+};
+
+static const struct pmc_dev_map dev_map[] = {
+	{"0  - LPSS1_F0_DMA",		BIT_LPSS1_F0_DMA},
+	{"1  - LPSS1_F1_PWM1",		BIT_LPSS1_F1_PWM1},
+	{"2  - LPSS1_F2_PWM2",		BIT_LPSS1_F2_PWM2},
+	{"3  - LPSS1_F3_HSUART1",	BIT_LPSS1_F3_HSUART1},
+	{"4  - LPSS1_F4_HSUART2",	BIT_LPSS1_F4_HSUART2},
+	{"5  - LPSS1_F5_SPI",		BIT_LPSS1_F5_SPI},
+	{"6  - LPSS1_F6_Reserved",	BIT_LPSS1_F6_XXX},
+	{"7  - LPSS1_F7_Reserved",	BIT_LPSS1_F7_XXX},
+	{"8  - SCC_EMMC",		BIT_SCC_EMMC},
+	{"9  - SCC_SDIO",		BIT_SCC_SDIO},
+	{"10 - SCC_SDCARD",		BIT_SCC_SDCARD},
+	{"11 - SCC_MIPI",		BIT_SCC_MIPI},
+	{"12 - HDA",			BIT_HDA},
+	{"13 - LPE",			BIT_LPE},
+	{"14 - OTG",			BIT_OTG},
+	{"15 - USH",			BIT_USH},
+	{"16 - GBE",			BIT_GBE},
+	{"17 - SATA",			BIT_SATA},
+	{"18 - USB_EHCI",		BIT_USB_EHCI},
+	{"19 - SEC",			BIT_SEC},
+	{"20 - PCIE_PORT0",		BIT_PCIE_PORT0},
+	{"21 - PCIE_PORT1",		BIT_PCIE_PORT1},
+	{"22 - PCIE_PORT2",		BIT_PCIE_PORT2},
+	{"23 - PCIE_PORT3",		BIT_PCIE_PORT3},
+	{"24 - LPSS2_F0_DMA",		BIT_LPSS2_F0_DMA},
+	{"25 - LPSS2_F1_I2C1",		BIT_LPSS2_F1_I2C1},
+	{"26 - LPSS2_F2_I2C2",		BIT_LPSS2_F2_I2C2},
+	{"27 - LPSS2_F3_I2C3",		BIT_LPSS2_F3_I2C3},
+	{"28 - LPSS2_F3_I2C4",		BIT_LPSS2_F4_I2C4},
+	{"29 - LPSS2_F5_I2C5",		BIT_LPSS2_F5_I2C5},
+	{"30 - LPSS2_F6_I2C6",		BIT_LPSS2_F6_I2C6},
+	{"31 - LPSS2_F7_I2C7",		BIT_LPSS2_F7_I2C7},
+	{"32 - SMB",			BIT_SMB},
+	{"33 - USH_SS_PHY",		BIT_OTG_SS_PHY},
+	{"34 - OTG_SS_PHY",		BIT_USH_SS_PHY},
+	{"35 - DFX",			BIT_DFX},
+};
+
+static inline u32 pmc_reg_read(struct pmc_dev *pmc, int reg_offset)
+{
+	return readl(pmc->regmap + reg_offset);
+}
+
+static void pmc_power_off(void)
+{
+	u16	pm1_cnt_port;
+	u32	pm1_cnt_value;
+
+	pr_info("Preparing to enter system sleep state S5\n");
+
+	pm1_cnt_port = acpi_base_addr + PM1_CNT;
+
+	pm1_cnt_value = inl(pm1_cnt_port);
+	pm1_cnt_value &= SLEEP_TYPE_MASK;
+	pm1_cnt_value |= SLEEP_TYPE_S5;
+	pm1_cnt_value |= SLEEP_ENABLE;
+
+	outl(pm1_cnt_value, pm1_cnt_port);
+}
+
+#ifdef CONFIG_DEBUG_FS
+static int pmc_dev_state_show(struct seq_file *s, void *unused)
+{
+	struct pmc_dev *pmc = (struct pmc_dev *)s->private;
+	u32 func_dis, func_dis_2, func_dis_index;
+	u32 d3_sts_0, d3_sts_1, d3_sts_index;
+	int dev_num, dev_index, reg_index;
+
+	func_dis = pmc_reg_read(pmc, PMC_FUNC_DIS);
+	func_dis_2 = pmc_reg_read(pmc, PMC_FUNC_DIS_2);
+	d3_sts_0 = pmc_reg_read(pmc, PMC_D3_STS_0);
+	d3_sts_1 = pmc_reg_read(pmc, PMC_D3_STS_1);
+
+	dev_num = sizeof(dev_map) / sizeof(struct pmc_dev_map);
+
+	for (dev_index = 0; dev_index < dev_num; dev_index++) {
+		reg_index = dev_index / PMC_REG_BIT_WIDTH;
+		if (reg_index) {
+			func_dis_index = func_dis_2;
+			d3_sts_index = d3_sts_1;
+		} else {
+			func_dis_index = func_dis;
+			d3_sts_index = d3_sts_0;
+		}
+
+		seq_printf(s, "Dev: %-32s\tState: %s [%s]\n",
+			dev_map[dev_index].name,
+			dev_map[dev_index].bit_mask & func_dis_index ?
+			"Disabled" : "Enabled ",
+			dev_map[dev_index].bit_mask & d3_sts_index ?
+			"D3" : "D0");
+	}
+	return 0;
+}
+
+static int pmc_dev_state_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, pmc_dev_state_show, inode->i_private);
+}
+
+static const struct file_operations pmc_dev_state_ops = {
+	.open		= pmc_dev_state_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int pmc_sleep_tmr_show(struct seq_file *s, void *unused)
+{
+	struct pmc_dev *pmc = (struct pmc_dev *)s->private;
+	u64 s0ir_tmr, s0i1_tmr, s0i2_tmr, s0i3_tmr, s0_tmr;
+
+	s0ir_tmr = pmc_reg_read(pmc, PMC_S0IR_TMR) << PMC_TMR_SHIFT;
+	s0i1_tmr = pmc_reg_read(pmc, PMC_S0I1_TMR) << PMC_TMR_SHIFT;
+	s0i2_tmr = pmc_reg_read(pmc, PMC_S0I2_TMR) << PMC_TMR_SHIFT;
+	s0i3_tmr = pmc_reg_read(pmc, PMC_S0I3_TMR) << PMC_TMR_SHIFT;
+	s0_tmr = pmc_reg_read(pmc, PMC_S0_TMR) << PMC_TMR_SHIFT;
+
+	seq_printf(s, "S0IR Residency:\t%lldus\n", s0ir_tmr);
+	seq_printf(s, "S0I1 Residency:\t%lldus\n", s0i1_tmr);
+	seq_printf(s, "S0I2 Residency:\t%lldus\n", s0i2_tmr);
+	seq_printf(s, "S0I3 Residency:\t%lldus\n", s0i3_tmr);
+	seq_printf(s, "S0   Residency:\t%lldus\n", s0_tmr);
+	return 0;
+}
+
+static int pmc_sleep_tmr_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, pmc_sleep_tmr_show, inode->i_private);
+}
+
+static const struct file_operations pmc_sleep_tmr_ops = {
+	.open		= pmc_sleep_tmr_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static void pmc_dbgfs_unregister(struct pmc_dev *pmc)
+{
+	if (!pmc->dbgfs_dir)
+		return;
+
+	debugfs_remove_recursive(pmc->dbgfs_dir);
+	pmc->dbgfs_dir = NULL;
+}
+
+static int pmc_dbgfs_register(struct pmc_dev *pmc)
+{
+	struct dentry *dir, *f;
+
+	dir = debugfs_create_dir("pmc_atom", NULL);
+	if (!dir)
+		return -ENOMEM;
+
+	f = debugfs_create_file("dev_state", S_IFREG | S_IRUGO,
+				dir, pmc, &pmc_dev_state_ops);
+	if (!f) {
+		dev_err(&pmc->pdev->dev, "dev_states register failed\n");
+		goto err;
+	}
+	f = debugfs_create_file("sleep_state", S_IFREG | S_IRUGO,
+				dir, pmc, &pmc_sleep_tmr_ops);
+	if (!f) {
+		dev_err(&pmc->pdev->dev, "sleep_state register failed\n");
+		goto err;
+	}
+	pmc->dbgfs_dir = dir;
+	return 0;
+err:
+	pmc_dbgfs_unregister(pmc);
+	return -ENODEV;
+}
+#endif /* CONFIG_DEBUG_FS */
+
+static int pmc_probe(struct pci_dev *pdev,
+			  const struct pci_device_id *unused)
+{
+	struct pmc_dev *pmc;
+	int ret;
+
+	ret = pci_enable_device(pdev);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "error: could not enable device\n");
+		goto err_enable_device;
+	}
+
+	ret = pci_request_regions(pdev, DRIVER_NAME);
+	if (ret) {
+		dev_err(&pdev->dev, "error: could not request PCI region\n");
+		goto err_request_regions;
+	}
+
+	pmc = devm_kzalloc(&pdev->dev, sizeof(struct pmc_dev), GFP_KERNEL);
+	if (!pmc) {
+		ret = -ENOMEM;
+		goto err_devm_kzalloc;
+	}
+
+	pmc->pdev = pci_dev_get(pdev);
+
+	pci_read_config_dword(pdev, PMC_BASE_ADDR_OFFSET, &pmc->base_addr);
+	pmc->base_addr &= PMC_BASE_ADDR_MASK;
+
+	pmc->regmap = devm_ioremap_nocache(&pdev->dev,
+		pmc->base_addr, PMC_MMIO_REG_LEN);
+	if (!pmc->regmap) {
+		dev_err(&pdev->dev, "error: ioremap failed\n");
+		ret = -ENOMEM;
+		goto err_devm_ioremap;
+	}
+	pci_set_drvdata(pdev, pmc);
+#ifdef CONFIG_DEBUG_FS
+	pmc_dbgfs_register(pmc);
+#endif /* CONFIG_DEBUG_FS */
+
+	/* Install power off function */
+	pci_read_config_dword(pdev, ACPI_BASE_ADDR_OFFSET, &acpi_base_addr);
+	acpi_base_addr &= ACPI_BASE_ADDR_MASK;
+	if (acpi_base_addr != 0 && pm_power_off == NULL)
+		pm_power_off = pmc_power_off;
+	return 0;
+err_devm_ioremap:
+	pci_dev_put(pdev);
+err_devm_kzalloc:
+	pci_release_regions(pdev);
+err_request_regions:
+	pci_disable_device(pdev);
+err_enable_device:
+	dev_err(&pdev->dev, "error: probe failed\n");
+	return ret;
+}
+
+static const struct pci_device_id pmc_pci_ids[] = {
+	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_VLV_PMC) },
+	{ 0, },
+};
+
+MODULE_DEVICE_TABLE(pci, pmc_pci_ids);
+
+static struct pci_driver pmc_pci_driver = {
+	.name		= DRIVER_NAME,
+	.probe		= pmc_probe,
+	.id_table	= pmc_pci_ids,
+};
+
+module_pci_driver(pmc_pci_driver);
+
+MODULE_AUTHOR("Aubrey Li <aubrey.li@linux.intel.com>");
+MODULE_DESCRIPTION("Intel Atom SOC Power Management Controller Interface");
+MODULE_LICENSE("GPL v2");
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2014-03-07  3:05 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-03-06  3:31 [PATCH] x86: new Intel Atom SoC power management controller driver Li, Aubrey
2014-03-06  3:49 ` H. Peter Anvin
2014-03-06  5:21   ` Li, Aubrey
2014-03-06  5:28     ` H. Peter Anvin
2014-03-06  5:33       ` Li, Aubrey
2014-03-06  5:52         ` H. Peter Anvin
2014-03-06  5:58           ` Li, Aubrey
2014-03-06 16:30             ` H. Peter Anvin
2014-03-07  0:26               ` Li, Aubrey
2014-03-07  0:41                 ` Joe Perches
2014-03-07  2:08                   ` Li, Aubrey
2014-03-07  2:21                     ` Joe Perches
2014-03-07  3:04                       ` Li, Aubrey

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox