diff options
author | Li, Aubrey <aubrey.li@linux.intel.com> | 2014-06-30 02:08:42 -0400 |
---|---|---|
committer | H. Peter Anvin <hpa@linux.intel.com> | 2014-07-25 17:11:29 -0400 |
commit | 93e5eadd1f6e7f45c31aa327c42ac52e4df5ff6f (patch) | |
tree | d72d68d835dc4bf65dea67893cc701c78c31b0d2 | |
parent | 832fcc899a90cce54eb5e47c7fd099eacc0130da (diff) |
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 Atom SoC. This driver provides
a native power off function via PMC PCI IO port.
On some ACPI hardware-reduced platforms(e.g. ASUS-T100), ACPI sleep
registers are not valid so that (*pm_power_off)() is not hooked by
acpi_power_off(). The power off function in this driver is installed
only when pm_power_off is NULL.
Signed-off-by: Aubrey Li <aubrey.li@linux.intel.com>
Link: http://lkml.kernel.org/r/53B0FEEA.3010805@linux.intel.com
Signed-off-by: Lejun Zhu <lejun.zhu@linux.intel.com>
Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
-rw-r--r-- | arch/x86/Kconfig | 4 | ||||
-rw-r--r-- | arch/x86/include/asm/pmc_atom.h | 31 | ||||
-rw-r--r-- | arch/x86/kernel/Makefile | 1 | ||||
-rw-r--r-- | arch/x86/kernel/pmc_atom.c | 104 |
4 files changed, 140 insertions, 0 deletions
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index a8f749ef0fdc..6295a2182a76 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig | |||
@@ -2403,6 +2403,10 @@ config IOSF_MBI | |||
2403 | default m | 2403 | default m |
2404 | depends on PCI | 2404 | depends on PCI |
2405 | 2405 | ||
2406 | config PMC_ATOM | ||
2407 | def_bool y | ||
2408 | depends on PCI | ||
2409 | |||
2406 | source "net/Kconfig" | 2410 | source "net/Kconfig" |
2407 | 2411 | ||
2408 | source "drivers/Kconfig" | 2412 | 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 000000000000..03a2769f2d7f --- /dev/null +++ b/arch/x86/include/asm/pmc_atom.h | |||
@@ -0,0 +1,31 @@ | |||
1 | /* | ||
2 | * Intel Atom SOC Power Management Controller Header File | ||
3 | * Copyright (c) 2014, Intel Corporation. | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms and conditions of the GNU General Public License, | ||
7 | * version 2, as published by the Free Software Foundation. | ||
8 | * | ||
9 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
12 | * more details. | ||
13 | * | ||
14 | */ | ||
15 | |||
16 | #ifndef PMC_ATOM_H | ||
17 | #define PMC_ATOM_H | ||
18 | |||
19 | /* ValleyView Power Control Unit PCI Device ID */ | ||
20 | #define PCI_DEVICE_ID_VLV_PMC 0x0F1C | ||
21 | |||
22 | /* PMC I/O Registers */ | ||
23 | #define ACPI_BASE_ADDR_OFFSET 0x40 | ||
24 | #define ACPI_BASE_ADDR_MASK 0xFFFFFE00 | ||
25 | #define ACPI_MMIO_REG_LEN 0x100 | ||
26 | |||
27 | #define PM1_CNT 0x4 | ||
28 | #define SLEEP_TYPE_MASK 0xFFFFECFF | ||
29 | #define SLEEP_TYPE_S5 0x1C00 | ||
30 | #define SLEEP_ENABLE 0x2000 | ||
31 | #endif /* PMC_ATOM_H */ | ||
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile index 047f9ff2e36c..bde3993624f1 100644 --- a/arch/x86/kernel/Makefile +++ b/arch/x86/kernel/Makefile | |||
@@ -106,6 +106,7 @@ obj-$(CONFIG_EFI) += sysfb_efi.o | |||
106 | obj-$(CONFIG_PERF_EVENTS) += perf_regs.o | 106 | obj-$(CONFIG_PERF_EVENTS) += perf_regs.o |
107 | obj-$(CONFIG_TRACING) += tracepoint.o | 107 | obj-$(CONFIG_TRACING) += tracepoint.o |
108 | obj-$(CONFIG_IOSF_MBI) += iosf_mbi.o | 108 | obj-$(CONFIG_IOSF_MBI) += iosf_mbi.o |
109 | obj-$(CONFIG_PMC_ATOM) += pmc_atom.o | ||
109 | 110 | ||
110 | ### | 111 | ### |
111 | # 64 bit specific files | 112 | # 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 000000000000..9eb79f6fc512 --- /dev/null +++ b/arch/x86/kernel/pmc_atom.c | |||
@@ -0,0 +1,104 @@ | |||
1 | /* | ||
2 | * Intel Atom SOC Power Management Controller Driver | ||
3 | * Copyright (c) 2014, Intel Corporation. | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms and conditions of the GNU General Public License, | ||
7 | * version 2, as published by the Free Software Foundation. | ||
8 | * | ||
9 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
12 | * more details. | ||
13 | * | ||
14 | */ | ||
15 | |||
16 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
17 | |||
18 | #include <linux/module.h> | ||
19 | #include <linux/init.h> | ||
20 | #include <linux/pci.h> | ||
21 | #include <linux/device.h> | ||
22 | #include <linux/io.h> | ||
23 | |||
24 | #include <asm/pmc_atom.h> | ||
25 | |||
26 | static u32 acpi_base_addr; | ||
27 | |||
28 | static void pmc_power_off(void) | ||
29 | { | ||
30 | u16 pm1_cnt_port; | ||
31 | u32 pm1_cnt_value; | ||
32 | |||
33 | pr_info("Preparing to enter system sleep state S5\n"); | ||
34 | |||
35 | pm1_cnt_port = acpi_base_addr + PM1_CNT; | ||
36 | |||
37 | pm1_cnt_value = inl(pm1_cnt_port); | ||
38 | pm1_cnt_value &= SLEEP_TYPE_MASK; | ||
39 | pm1_cnt_value |= SLEEP_TYPE_S5; | ||
40 | pm1_cnt_value |= SLEEP_ENABLE; | ||
41 | |||
42 | outl(pm1_cnt_value, pm1_cnt_port); | ||
43 | } | ||
44 | |||
45 | static int pmc_setup_dev(struct pci_dev *pdev) | ||
46 | { | ||
47 | /* Obtain ACPI base address */ | ||
48 | pci_read_config_dword(pdev, ACPI_BASE_ADDR_OFFSET, &acpi_base_addr); | ||
49 | acpi_base_addr &= ACPI_BASE_ADDR_MASK; | ||
50 | |||
51 | /* Install power off function */ | ||
52 | if (acpi_base_addr != 0 && pm_power_off == NULL) | ||
53 | pm_power_off = pmc_power_off; | ||
54 | |||
55 | return 0; | ||
56 | } | ||
57 | |||
58 | /* | ||
59 | * Data for PCI driver interface | ||
60 | * | ||
61 | * This data only exists for exporting the supported | ||
62 | * PCI ids via MODULE_DEVICE_TABLE. We do not actually | ||
63 | * register a pci_driver, because lpc_ich will register | ||
64 | * a driver on the same PCI id. | ||
65 | */ | ||
66 | static const struct pci_device_id pmc_pci_ids[] = { | ||
67 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_VLV_PMC) }, | ||
68 | { 0, }, | ||
69 | }; | ||
70 | |||
71 | MODULE_DEVICE_TABLE(pci, pmc_pci_ids); | ||
72 | |||
73 | static int __init pmc_atom_init(void) | ||
74 | { | ||
75 | int err = -ENODEV; | ||
76 | struct pci_dev *pdev = NULL; | ||
77 | const struct pci_device_id *ent; | ||
78 | |||
79 | /* We look for our device - PCU PMC | ||
80 | * we assume that there is max. one device. | ||
81 | * | ||
82 | * We can't use plain pci_driver mechanism, | ||
83 | * as the device is really a multiple function device, | ||
84 | * main driver that binds to the pci_device is lpc_ich | ||
85 | * and have to find & bind to the device this way. | ||
86 | */ | ||
87 | for_each_pci_dev(pdev) { | ||
88 | ent = pci_match_id(pmc_pci_ids, pdev); | ||
89 | if (ent) { | ||
90 | err = pmc_setup_dev(pdev); | ||
91 | goto out; | ||
92 | } | ||
93 | } | ||
94 | /* Device not found. */ | ||
95 | out: | ||
96 | return err; | ||
97 | } | ||
98 | |||
99 | module_init(pmc_atom_init); | ||
100 | /* no module_exit, this driver shouldn't be unloaded */ | ||
101 | |||
102 | MODULE_AUTHOR("Aubrey Li <aubrey.li@linux.intel.com>"); | ||
103 | MODULE_DESCRIPTION("Intel Atom SOC Power Management Controller Interface"); | ||
104 | MODULE_LICENSE("GPL v2"); | ||