xref: /linux/arch/x86/platform/atom/punit_atom_debug.c (revision 9acb51e9617c28a92f9ce2af767db6bd660a6d4f)
1  // SPDX-License-Identifier: GPL-2.0-only
2  /*
3   * Intel SOC Punit device state debug driver
4   * Punit controls power management for North Complex devices (Graphics
5   * blocks, Image Signal Processing, video processing, display, DSP etc.)
6   *
7   * Copyright (c) 2015, Intel Corporation.
8   */
9  
10  #define pr_fmt(fmt) "punit_atom: " fmt
11  
12  #include <linux/acpi.h>
13  #include <linux/module.h>
14  #include <linux/init.h>
15  #include <linux/device.h>
16  #include <linux/debugfs.h>
17  #include <linux/seq_file.h>
18  #include <linux/io.h>
19  #include <asm/cpu_device_id.h>
20  #include <asm/intel-family.h>
21  #include <asm/iosf_mbi.h>
22  
23  /* Subsystem config/status Video processor */
24  #define VED_SS_PM0		0x32
25  /* Subsystem config/status ISP (Image Signal Processor) */
26  #define ISP_SS_PM0		0x39
27  /* Subsystem config/status Input/output controller */
28  #define MIO_SS_PM		0x3B
29  /* Shift bits for getting status for video, isp and i/o */
30  #define SSS_SHIFT		24
31  
32  /* Power gate status reg */
33  #define PWRGT_STATUS		0x61
34  /* Shift bits for getting status for graphics rendering */
35  #define RENDER_POS		0
36  /* Shift bits for getting status for media control */
37  #define MEDIA_POS		2
38  /* Shift bits for getting status for Valley View/Baytrail display */
39  #define VLV_DISPLAY_POS		6
40  
41  /* Subsystem config/status display for Cherry Trail SOC */
42  #define CHT_DSP_SSS		0x36
43  /* Shift bits for getting status for display */
44  #define CHT_DSP_SSS_POS		16
45  
46  struct punit_device {
47  	char *name;
48  	int reg;
49  	int sss_pos;
50  };
51  
52  static const struct punit_device punit_device_tng[] = {
53  	{ "DISPLAY",	CHT_DSP_SSS,	SSS_SHIFT },
54  	{ "VED",	VED_SS_PM0,	SSS_SHIFT },
55  	{ "ISP",	ISP_SS_PM0,	SSS_SHIFT },
56  	{ "MIO",	MIO_SS_PM,	SSS_SHIFT },
57  	{ NULL }
58  };
59  
60  static const struct punit_device punit_device_byt[] = {
61  	{ "GFX RENDER",	PWRGT_STATUS,	RENDER_POS },
62  	{ "GFX MEDIA",	PWRGT_STATUS,	MEDIA_POS },
63  	{ "DISPLAY",	PWRGT_STATUS,	VLV_DISPLAY_POS },
64  	{ "VED",	VED_SS_PM0,	SSS_SHIFT },
65  	{ "ISP",	ISP_SS_PM0,	SSS_SHIFT },
66  	{ "MIO",	MIO_SS_PM,	SSS_SHIFT },
67  	{ NULL }
68  };
69  
70  static const struct punit_device punit_device_cht[] = {
71  	{ "GFX RENDER",	PWRGT_STATUS,	RENDER_POS },
72  	{ "GFX MEDIA",	PWRGT_STATUS,	MEDIA_POS },
73  	{ "DISPLAY",	CHT_DSP_SSS,	CHT_DSP_SSS_POS },
74  	{ "VED",	VED_SS_PM0,	SSS_SHIFT },
75  	{ "ISP",	ISP_SS_PM0,	SSS_SHIFT },
76  	{ "MIO",	MIO_SS_PM,	SSS_SHIFT },
77  	{ NULL }
78  };
79  
80  static const char * const dstates[] = {"D0", "D0i1", "D0i2", "D0i3"};
81  
82  static int punit_dev_state_show(struct seq_file *seq_file, void *unused)
83  {
84  	u32 punit_pwr_status;
85  	struct punit_device *punit_devp = seq_file->private;
86  	int index;
87  	int status;
88  
89  	seq_puts(seq_file, "\n\nPUNIT NORTH COMPLEX DEVICES :\n");
90  	while (punit_devp->name) {
91  		status = iosf_mbi_read(BT_MBI_UNIT_PMC, MBI_REG_READ,
92  				       punit_devp->reg, &punit_pwr_status);
93  		if (status) {
94  			seq_printf(seq_file, "%9s : Read Failed\n",
95  				   punit_devp->name);
96  		} else  {
97  			index = (punit_pwr_status >> punit_devp->sss_pos) & 3;
98  			seq_printf(seq_file, "%9s : %s\n", punit_devp->name,
99  				   dstates[index]);
100  		}
101  		punit_devp++;
102  	}
103  
104  	return 0;
105  }
106  DEFINE_SHOW_ATTRIBUTE(punit_dev_state);
107  
108  static struct dentry *punit_dbg_file;
109  
110  static void punit_dbgfs_register(struct punit_device *punit_device)
111  {
112  	punit_dbg_file = debugfs_create_dir("punit_atom", NULL);
113  
114  	debugfs_create_file("dev_power_state", 0444, punit_dbg_file,
115  			    punit_device, &punit_dev_state_fops);
116  }
117  
118  static void punit_dbgfs_unregister(void)
119  {
120  	debugfs_remove_recursive(punit_dbg_file);
121  }
122  
123  #if defined(CONFIG_ACPI) && defined(CONFIG_SUSPEND)
124  static const struct punit_device *punit_dev;
125  
126  static void punit_s2idle_check(void)
127  {
128  	const struct punit_device *punit_devp;
129  	u32 punit_pwr_status, dstate;
130  	int status;
131  
132  	for (punit_devp = punit_dev; punit_devp->name; punit_devp++) {
133  		/* Skip MIO, it is on till the very last moment */
134  		if (punit_devp->reg == MIO_SS_PM)
135  			continue;
136  
137  		status = iosf_mbi_read(BT_MBI_UNIT_PMC, MBI_REG_READ,
138  				       punit_devp->reg, &punit_pwr_status);
139  		if (status) {
140  			pr_err("%s read failed\n", punit_devp->name);
141  		} else  {
142  			dstate = (punit_pwr_status >> punit_devp->sss_pos) & 3;
143  			if (!dstate)
144  				pr_err("%s is in D0 prior to s2idle\n", punit_devp->name);
145  		}
146  	}
147  }
148  
149  static struct acpi_s2idle_dev_ops punit_s2idle_ops = {
150  	.check = punit_s2idle_check,
151  };
152  
153  static void punit_s2idle_check_register(struct punit_device *punit_device)
154  {
155  	punit_dev = punit_device;
156  	acpi_register_lps0_dev(&punit_s2idle_ops);
157  }
158  
159  static void punit_s2idle_check_unregister(void)
160  {
161  	acpi_unregister_lps0_dev(&punit_s2idle_ops);
162  }
163  #else
164  static void punit_s2idle_check_register(struct punit_device *punit_device) {}
165  static void punit_s2idle_check_unregister(void) {}
166  #endif
167  
168  #define X86_MATCH(vfm, data)					 \
169  	X86_MATCH_VFM_FEATURE(vfm, X86_FEATURE_MWAIT, data)
170  
171  static const struct x86_cpu_id intel_punit_cpu_ids[] = {
172  	X86_MATCH(INTEL_ATOM_SILVERMONT,	&punit_device_byt),
173  	X86_MATCH(INTEL_ATOM_SILVERMONT_MID,	&punit_device_tng),
174  	X86_MATCH(INTEL_ATOM_AIRMONT,		&punit_device_cht),
175  	{}
176  };
177  MODULE_DEVICE_TABLE(x86cpu, intel_punit_cpu_ids);
178  
179  static int __init punit_atom_debug_init(void)
180  {
181  	struct punit_device *punit_device;
182  	const struct x86_cpu_id *id;
183  
184  	id = x86_match_cpu(intel_punit_cpu_ids);
185  	if (!id)
186  		return -ENODEV;
187  
188  	punit_device = (struct punit_device *)id->driver_data;
189  	punit_dbgfs_register(punit_device);
190  	punit_s2idle_check_register(punit_device);
191  
192  	return 0;
193  }
194  
195  static void __exit punit_atom_debug_exit(void)
196  {
197  	punit_s2idle_check_unregister();
198  	punit_dbgfs_unregister();
199  }
200  
201  module_init(punit_atom_debug_init);
202  module_exit(punit_atom_debug_exit);
203  
204  MODULE_AUTHOR("Kumar P, Mahesh <mahesh.kumar.p@intel.com>");
205  MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
206  MODULE_DESCRIPTION("Driver for Punit devices states debugging");
207  MODULE_LICENSE("GPL v2");
208