1/*
2 * Intel SOC Punit device state debug driver
3 * Punit controls power management for North Complex devices (Graphics
4 * blocks, Image Signal Processing, video processing, display, DSP etc.)
5 *
6 * Copyright (c) 2015, Intel Corporation.
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms and conditions of the GNU General Public License,
10 * version 2, as published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
15 * more details.
16 *
17 */
18
19#include <linux/module.h>
20#include <linux/init.h>
21#include <linux/device.h>
22#include <linux/debugfs.h>
23#include <linux/seq_file.h>
24#include <linux/io.h>
25#include <asm/cpu_device_id.h>
26#include <asm/iosf_mbi.h>
27
28/* Side band Interface port */
29#define PUNIT_PORT		0x04
30/* Power gate status reg */
31#define PWRGT_STATUS		0x61
32/* Subsystem config/status Video processor */
33#define VED_SS_PM0		0x32
34/* Subsystem config/status ISP (Image Signal Processor) */
35#define ISP_SS_PM0		0x39
36/* Subsystem config/status Input/output controller */
37#define MIO_SS_PM		0x3B
38/* Shift bits for getting status for video, isp and i/o */
39#define SSS_SHIFT		24
40/* Shift bits for getting status for graphics rendering */
41#define RENDER_POS		0
42/* Shift bits for getting status for media control */
43#define MEDIA_POS		2
44/* Shift bits for getting status for Valley View/Baytrail display */
45#define VLV_DISPLAY_POS		6
46/* Subsystem config/status display for Cherry Trail SOC */
47#define CHT_DSP_SSS		0x36
48/* Shift bits for getting status for display */
49#define CHT_DSP_SSS_POS		16
50
51struct punit_device {
52	char *name;
53	int reg;
54	int sss_pos;
55};
56
57static const struct punit_device punit_device_byt[] = {
58	{ "GFX RENDER",	PWRGT_STATUS,	RENDER_POS },
59	{ "GFX MEDIA",	PWRGT_STATUS,	MEDIA_POS },
60	{ "DISPLAY",	PWRGT_STATUS,	VLV_DISPLAY_POS },
61	{ "VED",	VED_SS_PM0,	SSS_SHIFT },
62	{ "ISP",	ISP_SS_PM0,	SSS_SHIFT },
63	{ "MIO",	MIO_SS_PM,	SSS_SHIFT },
64	{ NULL }
65};
66
67static const struct punit_device punit_device_cht[] = {
68	{ "GFX RENDER",	PWRGT_STATUS,	RENDER_POS },
69	{ "GFX MEDIA",	PWRGT_STATUS,	MEDIA_POS },
70	{ "DISPLAY",	CHT_DSP_SSS,	CHT_DSP_SSS_POS },
71	{ "VED",	VED_SS_PM0,	SSS_SHIFT },
72	{ "ISP",	ISP_SS_PM0,	SSS_SHIFT },
73	{ "MIO",	MIO_SS_PM,	SSS_SHIFT },
74	{ NULL }
75};
76
77static const char * const dstates[] = {"D0", "D0i1", "D0i2", "D0i3"};
78
79static int punit_dev_state_show(struct seq_file *seq_file, void *unused)
80{
81	u32 punit_pwr_status;
82	struct punit_device *punit_devp = seq_file->private;
83	int index;
84	int status;
85
86	seq_puts(seq_file, "\n\nPUNIT NORTH COMPLEX DEVICES :\n");
87	while (punit_devp->name) {
88		status = iosf_mbi_read(PUNIT_PORT, BT_MBI_PMC_READ,
89				       punit_devp->reg,
90				       &punit_pwr_status);
91		if (status) {
92			seq_printf(seq_file, "%9s : Read Failed\n",
93				   punit_devp->name);
94		} else  {
95			index = (punit_pwr_status >> punit_devp->sss_pos) & 3;
96			seq_printf(seq_file, "%9s : %s\n", punit_devp->name,
97				   dstates[index]);
98		}
99		punit_devp++;
100	}
101
102	return 0;
103}
104
105static int punit_dev_state_open(struct inode *inode, struct file *file)
106{
107	return single_open(file, punit_dev_state_show, inode->i_private);
108}
109
110static const struct file_operations punit_dev_state_ops = {
111	.open		= punit_dev_state_open,
112	.read		= seq_read,
113	.llseek		= seq_lseek,
114	.release	= single_release,
115};
116
117static struct dentry *punit_dbg_file;
118
119static int punit_dbgfs_register(struct punit_device *punit_device)
120{
121	static struct dentry *dev_state;
122
123	punit_dbg_file = debugfs_create_dir("punit_atom", NULL);
124	if (!punit_dbg_file)
125		return -ENXIO;
126
127	dev_state = debugfs_create_file("dev_power_state", S_IFREG | S_IRUGO,
128					punit_dbg_file, punit_device,
129					&punit_dev_state_ops);
130	if (!dev_state) {
131		pr_err("punit_dev_state register failed\n");
132		debugfs_remove(punit_dbg_file);
133		return -ENXIO;
134	}
135
136	return 0;
137}
138
139static void punit_dbgfs_unregister(void)
140{
141	debugfs_remove_recursive(punit_dbg_file);
142}
143
144#define ICPU(model, drv_data) \
145	{ X86_VENDOR_INTEL, 6, model, X86_FEATURE_MWAIT,\
146	  (kernel_ulong_t)&drv_data }
147
148static const struct x86_cpu_id intel_punit_cpu_ids[] = {
149	ICPU(55, punit_device_byt), /* Valleyview, Bay Trail */
150	ICPU(76, punit_device_cht), /* Braswell, Cherry Trail */
151	{}
152};
153
154MODULE_DEVICE_TABLE(x86cpu, intel_punit_cpu_ids);
155
156static int __init punit_atom_debug_init(void)
157{
158	const struct x86_cpu_id *id;
159	int ret;
160
161	id = x86_match_cpu(intel_punit_cpu_ids);
162	if (!id)
163		return -ENODEV;
164
165	ret = punit_dbgfs_register((struct punit_device *)id->driver_data);
166	if (ret < 0)
167		return ret;
168
169	return 0;
170}
171
172static void __exit punit_atom_debug_exit(void)
173{
174	punit_dbgfs_unregister();
175}
176
177module_init(punit_atom_debug_init);
178module_exit(punit_atom_debug_exit);
179
180MODULE_AUTHOR("Kumar P, Mahesh <mahesh.kumar.p@intel.com>");
181MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
182MODULE_DESCRIPTION("Driver for Punit devices states debugging");
183MODULE_LICENSE("GPL v2");
184