Skip to content

Commit 5b2853e

Browse files
js202510jwrdegoede
authored andcommitted
Intel Atom suspend: add debug check for S0ix blockers
Add a notifier called just before entering S0ix and register two callbacks for checking if all hardware blocks in the north complex (controller by punit) and south complex (controlled by pmc) are in a state that allows the SoC to enter package S0ix. Print an error message for any device in D0. This is a port of patches [PATCH 1/2] pm: Add pm suspend debug notifier for South IPs [PATCH 2/2] pm: Add pm suspend debug notifier for North IPs from https://github.com/MiCode/Xiaomi_Kernel_OpenSource latte-l-oss Signed-off-by: Johannes Stezenbach <js@sig21.net> Signed-off-by: Takashi Iwai <tiwai@suse.de> [hdegoede: Add pmc-clock checks, ignore fused off blocks, PMIC I2C] Signed-off-by: Hans de Goede <hdegoede@redhat.com>
1 parent 242b2ba commit 5b2853e

File tree

4 files changed

+183
-0
lines changed

4 files changed

+183
-0
lines changed

arch/x86/include/asm/intel_idle.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#ifndef _ASM_X86_INTEL_IDLE_H
2+
#define _ASM_X86_INTEL_IDLE_H
3+
4+
#include <linux/notifier.h>
5+
6+
#ifdef CONFIG_PM_DEBUG
7+
void intel_idle_freeze_notifier_register(struct notifier_block *nb);
8+
void intel_idle_freeze_notifier_unregister(struct notifier_block *nb);
9+
#endif
10+
11+
12+
#endif /* _ASM_X86_INTEL_IDLE_H */

arch/x86/platform/atom/punit_atom_debug.c

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <asm/cpu_device_id.h>
1717
#include <asm/intel-family.h>
1818
#include <asm/iosf_mbi.h>
19+
#include <asm/intel_idle.h>
1920

2021
/* Subsystem config/status Video processor */
2122
#define VED_SS_PM0 0x32
@@ -117,6 +118,47 @@ static void punit_dbgfs_unregister(void)
117118
debugfs_remove_recursive(punit_dbg_file);
118119
}
119120

121+
#if defined(CONFIG_PM_DEBUG) && defined(CONFIG_INTEL_IDLE)
122+
struct punit_notifier_block {
123+
struct notifier_block nb;
124+
struct punit_device *punit_device;
125+
};
126+
127+
static int punit_freeze_cb(struct notifier_block *nb,
128+
unsigned long action, void *data)
129+
{
130+
struct punit_notifier_block *punit_nb =
131+
container_of(nb, struct punit_notifier_block, nb);
132+
struct punit_device *punit_devp = punit_nb->punit_device;
133+
u32 punit_pwr_status;
134+
int index;
135+
int status;
136+
int cpu = action;
137+
138+
while (punit_devp->name) {
139+
status = iosf_mbi_read(BT_MBI_UNIT_PMC, MBI_REG_READ,
140+
punit_devp->reg, &punit_pwr_status);
141+
if (status) {
142+
pr_err("punit debug: %s: read failed\n",
143+
punit_devp->name);
144+
} else {
145+
index = (punit_pwr_status >> punit_devp->sss_pos) & 3;
146+
if (!index)
147+
pr_err("punit debug: cpu %d: %s is in D0 prior to freeze\n",
148+
cpu, punit_devp->name);
149+
}
150+
punit_devp++;
151+
}
152+
return 0;
153+
}
154+
155+
static struct punit_notifier_block punit_freeze_nb = {
156+
.nb = {
157+
.notifier_call = punit_freeze_cb,
158+
},
159+
};
160+
#endif
161+
120162
#define X86_MATCH(model, data) \
121163
X86_MATCH_VENDOR_FAM_MODEL_FEATURE(INTEL, 6, INTEL_FAM6_##model, \
122164
X86_FEATURE_MWAIT, data)
@@ -139,11 +181,18 @@ static int __init punit_atom_debug_init(void)
139181

140182
punit_dbgfs_register((struct punit_device *)id->driver_data);
141183

184+
#if defined(CONFIG_PM_DEBUG) && defined(CONFIG_INTEL_IDLE)
185+
punit_freeze_nb.punit_device = (struct punit_device *)id->driver_data;
186+
intel_idle_freeze_notifier_register(&punit_freeze_nb.nb);
187+
#endif
142188
return 0;
143189
}
144190

145191
static void __exit punit_atom_debug_exit(void)
146192
{
193+
#if defined(CONFIG_PM_DEBUG) && defined(CONFIG_INTEL_IDLE)
194+
intel_idle_freeze_notifier_unregister(&punit_freeze_nb.nb);
195+
#endif
147196
punit_dbgfs_unregister();
148197
}
149198

drivers/idle/intel_idle.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
#include <linux/moduleparam.h>
5353
#include <asm/cpu_device_id.h>
5454
#include <asm/intel-family.h>
55+
#include <asm/intel_idle.h>
5556
#include <asm/mwait.h>
5657
#include <asm/msr.h>
5758

@@ -103,6 +104,22 @@ static unsigned int mwait_substates __initdata;
103104
#define flg2MWAIT(flags) (((flags) >> 24) & 0xFF)
104105
#define MWAIT2flg(eax) ((eax & 0xFF) << 24)
105106

107+
#ifdef CONFIG_PM_DEBUG
108+
static RAW_NOTIFIER_HEAD(intel_idle_freeze_notifier);
109+
110+
void intel_idle_freeze_notifier_register(struct notifier_block *nb)
111+
{
112+
raw_notifier_chain_register(&intel_idle_freeze_notifier, nb);
113+
}
114+
EXPORT_SYMBOL(intel_idle_freeze_notifier_register);
115+
116+
void intel_idle_freeze_notifier_unregister(struct notifier_block *nb)
117+
{
118+
raw_notifier_chain_unregister(&intel_idle_freeze_notifier, nb);
119+
}
120+
EXPORT_SYMBOL(intel_idle_freeze_notifier_unregister);
121+
#endif
122+
106123
/**
107124
* intel_idle - Ask the processor to enter the given idle state.
108125
* @dev: cpuidle device of the target CPU.
@@ -150,6 +167,9 @@ static __cpuidle int intel_idle_s2idle(struct cpuidle_device *dev,
150167
unsigned long eax = flg2MWAIT(drv->states[index].flags);
151168
unsigned long ecx = 1; /* break on interrupt flag */
152169

170+
#ifdef CONFIG_PM_DEBUG
171+
raw_notifier_call_chain(&intel_idle_freeze_notifier, dev->cpu, NULL);
172+
#endif
153173
mwait_idle_with_hints(eax, ecx);
154174

155175
return 0;

drivers/platform/x86/pmc_atom.c

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,15 @@
1616
#include <linux/platform_device.h>
1717
#include <linux/pci.h>
1818
#include <linux/seq_file.h>
19+
#include <asm/intel_idle.h>
20+
21+
#define PMC_CLK_CTL_OFFSET 0x60
22+
#define PMC_CLK_NUM 6
23+
#define PMC_CLK_CTL_GATED_ON_D3 0x0
24+
#define PMC_CLK_CTL_FORCE_ON 0x1
25+
#define PMC_CLK_CTL_FORCE_OFF 0x2
26+
#define PMC_CLK_CTL_RESERVED 0x3
27+
#define PMC_MASK_CLK_CTL GENMASK(1, 0)
1928

2029
struct pmc_bit_map {
2130
const char *name;
@@ -479,6 +488,94 @@ static int pmc_setup_clks(struct pci_dev *pdev, void __iomem *pmc_regmap,
479488
return 0;
480489
}
481490

491+
#if defined(CONFIG_PM_DEBUG) && defined(CONFIG_INTEL_IDLE)
492+
struct pmc_notifier_block {
493+
struct notifier_block nb;
494+
struct pmc_dev *pmc;
495+
};
496+
497+
static void pmc_dev_state_check(int cpu, u32 sts, const struct pmc_bit_map *sts_map,
498+
u32 fd, const struct pmc_bit_map *fd_map,
499+
u32 sts_possible_false_pos)
500+
{
501+
int index;
502+
503+
for (index = 0; sts_map[index].name; index++) {
504+
if (!(fd_map[index].bit_mask & fd) &&
505+
!(sts_map[index].bit_mask & sts)) {
506+
if (sts_map[index].bit_mask & sts_possible_false_pos)
507+
pr_debug("pmc_atom: cpu %d: %s is in D0 prior to freeze\n",
508+
cpu, sts_map[index].name);
509+
else
510+
pr_err("pmc_atom: cpu %d: %s is in D0 prior to freeze\n",
511+
cpu, sts_map[index].name);
512+
}
513+
}
514+
}
515+
516+
static int pmc_freeze_cb(struct notifier_block *nb,
517+
unsigned long action, void *data)
518+
{
519+
struct pmc_notifier_block *pmc_nb =
520+
container_of(nb, struct pmc_notifier_block, nb);
521+
struct pmc_dev *pmc = pmc_nb->pmc;
522+
const struct pmc_reg_map *m = pmc->map;
523+
u32 func_dis, func_dis_2;
524+
u32 d3_sts_0, d3_sts_1;
525+
u32 false_pos_sts_0, false_pos_sts_1;
526+
int cpu = action;
527+
int i;
528+
529+
func_dis = pmc_reg_read(pmc, PMC_FUNC_DIS);
530+
func_dis_2 = pmc_reg_read(pmc, PMC_FUNC_DIS_2);
531+
d3_sts_0 = pmc_reg_read(pmc, PMC_D3_STS_0);
532+
d3_sts_1 = pmc_reg_read(pmc, PMC_D3_STS_1);
533+
534+
/*
535+
* Some blocks are not used on lower-featured versions of the SoC and
536+
* always report D0, these masks makes us log these at debug lvl.
537+
*/
538+
if (m->d3_sts_1 == byt_d3_sts_1_map) {
539+
/* BYT */
540+
false_pos_sts_0 = BIT_GBE | BIT_SATA | BIT_PCIE_PORT0 |
541+
BIT_PCIE_PORT1 | BIT_PCIE_PORT2 | BIT_PCIE_PORT3 |
542+
BIT_LPSS2_F5_I2C5;
543+
false_pos_sts_1 = BIT_SMB | BIT_USH_SS_PHY | BIT_DFX;
544+
} else {
545+
/* CHT */
546+
false_pos_sts_0 = BIT_GBE | BIT_SATA | BIT_LPSS2_F7_I2C7;
547+
false_pos_sts_1 = BIT_SMB | BIT_STS_ISH;
548+
}
549+
550+
/* Low part */
551+
pmc_dev_state_check(cpu, d3_sts_0, m->d3_sts_0, func_dis, m->func_dis,
552+
false_pos_sts_0);
553+
554+
/* High part */
555+
pmc_dev_state_check(cpu, d3_sts_1, m->d3_sts_1, func_dis_2, m->func_dis_2,
556+
false_pos_sts_1);
557+
558+
/* Check PMC clocks */
559+
for (i = 0; i < PMC_CLK_NUM; i++) {
560+
u32 ctl = pmc_reg_read(pmc, PMC_CLK_CTL_OFFSET + 4 * i);
561+
562+
if ((ctl & PMC_MASK_CLK_CTL) != PMC_CLK_CTL_FORCE_ON)
563+
continue;
564+
565+
pr_err("pmc debug: cpu %d: clk %d is ON prior to freeze (ctl %08x)\n",
566+
cpu, i, ctl);
567+
}
568+
569+
return 0;
570+
}
571+
572+
static struct pmc_notifier_block pmc_freeze_nb = {
573+
.nb = {
574+
.notifier_call = pmc_freeze_cb,
575+
},
576+
};
577+
#endif
578+
482579
static int pmc_setup_dev(struct pci_dev *pdev, const struct pci_device_id *ent)
483580
{
484581
struct pmc_dev *pmc = &pmc_device;
@@ -516,6 +613,11 @@ static int pmc_setup_dev(struct pci_dev *pdev, const struct pci_device_id *ent)
516613
dev_warn(&pdev->dev, "platform clocks register failed: %d\n",
517614
ret);
518615

616+
#if defined(CONFIG_PM_DEBUG) && defined(CONFIG_INTEL_IDLE)
617+
pmc_freeze_nb.pmc = pmc;
618+
intel_idle_freeze_notifier_register(&pmc_freeze_nb.nb);
619+
#endif
620+
519621
pmc->init = true;
520622
return ret;
521623
}

0 commit comments

Comments
 (0)