| 1 | |
| 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 |
|
| 24 | #define VED_SS_PM0 0x32
|
| 25 |
|
| 26 | #define ISP_SS_PM0 0x39
|
| 27 |
|
| 28 | #define MIO_SS_PM 0x3B
|
| 29 |
|
| 30 | #define SSS_SHIFT 24
|
| 31 |
|
| 32 |
|
| 33 | #define PWRGT_STATUS 0x61
|
| 34 |
|
| 35 | #define RENDER_POS 0
|
| 36 |
|
| 37 | #define MEDIA_POS 2
|
| 38 |
|
| 39 | #define VLV_DISPLAY_POS 6
|
| 40 |
|
| 41 |
|
| 42 | #define CHT_DSP_SSS 0x36
|
| 43 |
|
| 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 |
|
| 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 <[email protected]>");
|
| 205 | MODULE_AUTHOR("Srinivas Pandruvada <[email protected]>");
|
| 206 | MODULE_DESCRIPTION("Driver for Punit devices states debugging");
|
| 207 | MODULE_LICENSE("GPL v2");
|