414 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (c) 2020, The Linux Foundation. All rights reserved.4 *5 */6 7#include <linux/debugfs.h>8#include <linux/device.h>9#include <linux/interrupt.h>10#include <linux/list.h>11#include <linux/mhi.h>12#include <linux/module.h>13#include "internal.h"14 15static int mhi_debugfs_states_show(struct seq_file *m, void *d)16{17 struct mhi_controller *mhi_cntrl = m->private;18 19 /* states */20 seq_printf(m, "PM state: %s Device: %s MHI state: %s EE: %s wake: %s\n",21 to_mhi_pm_state_str(mhi_cntrl->pm_state),22 mhi_is_active(mhi_cntrl) ? "Active" : "Inactive",23 mhi_state_str(mhi_cntrl->dev_state),24 TO_MHI_EXEC_STR(mhi_cntrl->ee),25 mhi_cntrl->wake_set ? "true" : "false");26 27 /* counters */28 seq_printf(m, "M0: %u M2: %u M3: %u", mhi_cntrl->M0, mhi_cntrl->M2,29 mhi_cntrl->M3);30 31 seq_printf(m, " device wake: %u pending packets: %u\n",32 atomic_read(&mhi_cntrl->dev_wake),33 atomic_read(&mhi_cntrl->pending_pkts));34 35 return 0;36}37 38static int mhi_debugfs_events_show(struct seq_file *m, void *d)39{40 struct mhi_controller *mhi_cntrl = m->private;41 struct mhi_event *mhi_event;42 struct mhi_event_ctxt *er_ctxt;43 int i;44 45 if (!mhi_is_active(mhi_cntrl)) {46 seq_puts(m, "Device not ready\n");47 return -ENODEV;48 }49 50 er_ctxt = mhi_cntrl->mhi_ctxt->er_ctxt;51 mhi_event = mhi_cntrl->mhi_event;52 for (i = 0; i < mhi_cntrl->total_ev_rings;53 i++, er_ctxt++, mhi_event++) {54 struct mhi_ring *ring = &mhi_event->ring;55 56 if (mhi_event->offload_ev) {57 seq_printf(m, "Index: %d is an offload event ring\n",58 i);59 continue;60 }61 62 seq_printf(m, "Index: %d intmod count: %lu time: %lu",63 i, (le32_to_cpu(er_ctxt->intmod) & EV_CTX_INTMODC_MASK) >>64 __ffs(EV_CTX_INTMODC_MASK),65 (le32_to_cpu(er_ctxt->intmod) & EV_CTX_INTMODT_MASK) >>66 __ffs(EV_CTX_INTMODT_MASK));67 68 seq_printf(m, " base: 0x%0llx len: 0x%llx", le64_to_cpu(er_ctxt->rbase),69 le64_to_cpu(er_ctxt->rlen));70 71 seq_printf(m, " rp: 0x%llx wp: 0x%llx", le64_to_cpu(er_ctxt->rp),72 le64_to_cpu(er_ctxt->wp));73 74 seq_printf(m, " local rp: 0x%pK db: 0x%pad\n", ring->rp,75 &mhi_event->db_cfg.db_val);76 }77 78 return 0;79}80 81static int mhi_debugfs_channels_show(struct seq_file *m, void *d)82{83 struct mhi_controller *mhi_cntrl = m->private;84 struct mhi_chan *mhi_chan;85 struct mhi_chan_ctxt *chan_ctxt;86 int i;87 88 if (!mhi_is_active(mhi_cntrl)) {89 seq_puts(m, "Device not ready\n");90 return -ENODEV;91 }92 93 mhi_chan = mhi_cntrl->mhi_chan;94 chan_ctxt = mhi_cntrl->mhi_ctxt->chan_ctxt;95 for (i = 0; i < mhi_cntrl->max_chan; i++, chan_ctxt++, mhi_chan++) {96 struct mhi_ring *ring = &mhi_chan->tre_ring;97 98 if (mhi_chan->offload_ch) {99 seq_printf(m, "%s(%u) is an offload channel\n",100 mhi_chan->name, mhi_chan->chan);101 continue;102 }103 104 if (!mhi_chan->mhi_dev)105 continue;106 107 seq_printf(m,108 "%s(%u) state: 0x%lx brstmode: 0x%lx pollcfg: 0x%lx",109 mhi_chan->name, mhi_chan->chan, (le32_to_cpu(chan_ctxt->chcfg) &110 CHAN_CTX_CHSTATE_MASK) >> __ffs(CHAN_CTX_CHSTATE_MASK),111 (le32_to_cpu(chan_ctxt->chcfg) & CHAN_CTX_BRSTMODE_MASK) >>112 __ffs(CHAN_CTX_BRSTMODE_MASK), (le32_to_cpu(chan_ctxt->chcfg) &113 CHAN_CTX_POLLCFG_MASK) >> __ffs(CHAN_CTX_POLLCFG_MASK));114 115 seq_printf(m, " type: 0x%x event ring: %u", le32_to_cpu(chan_ctxt->chtype),116 le32_to_cpu(chan_ctxt->erindex));117 118 seq_printf(m, " base: 0x%llx len: 0x%llx rp: 0x%llx wp: 0x%llx",119 le64_to_cpu(chan_ctxt->rbase), le64_to_cpu(chan_ctxt->rlen),120 le64_to_cpu(chan_ctxt->rp), le64_to_cpu(chan_ctxt->wp));121 122 seq_printf(m, " local rp: 0x%pK local wp: 0x%pK db: 0x%pad\n",123 ring->rp, ring->wp,124 &mhi_chan->db_cfg.db_val);125 }126 127 return 0;128}129 130static int mhi_device_info_show(struct device *dev, void *data)131{132 struct mhi_device *mhi_dev;133 134 if (dev->bus != &mhi_bus_type)135 return 0;136 137 mhi_dev = to_mhi_device(dev);138 139 seq_printf((struct seq_file *)data, "%s: type: %s dev_wake: %u",140 mhi_dev->name, mhi_dev->dev_type ? "Controller" : "Transfer",141 mhi_dev->dev_wake);142 143 /* for transfer device types only */144 if (mhi_dev->dev_type == MHI_DEVICE_XFER)145 seq_printf((struct seq_file *)data, " channels: %u(UL)/%u(DL)",146 mhi_dev->ul_chan_id, mhi_dev->dl_chan_id);147 148 seq_puts((struct seq_file *)data, "\n");149 150 return 0;151}152 153static int mhi_debugfs_devices_show(struct seq_file *m, void *d)154{155 struct mhi_controller *mhi_cntrl = m->private;156 157 if (!mhi_is_active(mhi_cntrl)) {158 seq_puts(m, "Device not ready\n");159 return -ENODEV;160 }161 162 /* Show controller and client(s) info */163 mhi_device_info_show(&mhi_cntrl->mhi_dev->dev, m);164 device_for_each_child(&mhi_cntrl->mhi_dev->dev, m, mhi_device_info_show);165 166 return 0;167}168 169static int mhi_debugfs_regdump_show(struct seq_file *m, void *d)170{171 struct mhi_controller *mhi_cntrl = m->private;172 enum mhi_state state;173 enum mhi_ee_type ee;174 int i, ret = -EIO;175 u32 val;176 void __iomem *mhi_base = mhi_cntrl->regs;177 void __iomem *bhi_base = mhi_cntrl->bhi;178 void __iomem *bhie_base = mhi_cntrl->bhie;179 void __iomem *wake_db = mhi_cntrl->wake_db;180 struct {181 const char *name;182 int offset;183 void __iomem *base;184 } regs[] = {185 { "MHI_REGLEN", MHIREGLEN, mhi_base},186 { "MHI_VER", MHIVER, mhi_base},187 { "MHI_CFG", MHICFG, mhi_base},188 { "MHI_CTRL", MHICTRL, mhi_base},189 { "MHI_STATUS", MHISTATUS, mhi_base},190 { "MHI_WAKE_DB", 0, wake_db},191 { "BHI_EXECENV", BHI_EXECENV, bhi_base},192 { "BHI_STATUS", BHI_STATUS, bhi_base},193 { "BHI_ERRCODE", BHI_ERRCODE, bhi_base},194 { "BHI_ERRDBG1", BHI_ERRDBG1, bhi_base},195 { "BHI_ERRDBG2", BHI_ERRDBG2, bhi_base},196 { "BHI_ERRDBG3", BHI_ERRDBG3, bhi_base},197 { "BHIE_TXVEC_DB", BHIE_TXVECDB_OFFS, bhie_base},198 { "BHIE_TXVEC_STATUS", BHIE_TXVECSTATUS_OFFS, bhie_base},199 { "BHIE_RXVEC_DB", BHIE_RXVECDB_OFFS, bhie_base},200 { "BHIE_RXVEC_STATUS", BHIE_RXVECSTATUS_OFFS, bhie_base},201 { NULL },202 };203 204 if (!MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state))205 return ret;206 207 seq_printf(m, "Host PM state: %s Device state: %s EE: %s\n",208 to_mhi_pm_state_str(mhi_cntrl->pm_state),209 mhi_state_str(mhi_cntrl->dev_state),210 TO_MHI_EXEC_STR(mhi_cntrl->ee));211 212 state = mhi_get_mhi_state(mhi_cntrl);213 ee = mhi_get_exec_env(mhi_cntrl);214 seq_printf(m, "Device EE: %s state: %s\n", TO_MHI_EXEC_STR(ee),215 mhi_state_str(state));216 217 for (i = 0; regs[i].name; i++) {218 if (!regs[i].base)219 continue;220 ret = mhi_read_reg(mhi_cntrl, regs[i].base, regs[i].offset,221 &val);222 if (ret)223 continue;224 225 seq_printf(m, "%s: 0x%x\n", regs[i].name, val);226 }227 228 return 0;229}230 231static int mhi_debugfs_device_wake_show(struct seq_file *m, void *d)232{233 struct mhi_controller *mhi_cntrl = m->private;234 struct mhi_device *mhi_dev = mhi_cntrl->mhi_dev;235 236 if (!mhi_is_active(mhi_cntrl)) {237 seq_puts(m, "Device not ready\n");238 return -ENODEV;239 }240 241 seq_printf(m,242 "Wake count: %d\n%s\n", mhi_dev->dev_wake,243 "Usage: echo get/put > device_wake to vote/unvote for M0");244 245 return 0;246}247 248static ssize_t mhi_debugfs_device_wake_write(struct file *file,249 const char __user *ubuf,250 size_t count, loff_t *ppos)251{252 struct seq_file *m = file->private_data;253 struct mhi_controller *mhi_cntrl = m->private;254 struct mhi_device *mhi_dev = mhi_cntrl->mhi_dev;255 char buf[16];256 int ret = -EINVAL;257 258 if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))259 return -EFAULT;260 261 if (!strncmp(buf, "get", 3)) {262 ret = mhi_device_get_sync(mhi_dev);263 } else if (!strncmp(buf, "put", 3)) {264 mhi_device_put(mhi_dev);265 ret = 0;266 }267 268 return ret ? ret : count;269}270 271static int mhi_debugfs_timeout_ms_show(struct seq_file *m, void *d)272{273 struct mhi_controller *mhi_cntrl = m->private;274 275 seq_printf(m, "%u ms\n", mhi_cntrl->timeout_ms);276 277 return 0;278}279 280static ssize_t mhi_debugfs_timeout_ms_write(struct file *file,281 const char __user *ubuf,282 size_t count, loff_t *ppos)283{284 struct seq_file *m = file->private_data;285 struct mhi_controller *mhi_cntrl = m->private;286 u32 timeout_ms;287 288 if (kstrtou32_from_user(ubuf, count, 0, &timeout_ms))289 return -EINVAL;290 291 mhi_cntrl->timeout_ms = timeout_ms;292 293 return count;294}295 296static int mhi_debugfs_states_open(struct inode *inode, struct file *fp)297{298 return single_open(fp, mhi_debugfs_states_show, inode->i_private);299}300 301static int mhi_debugfs_events_open(struct inode *inode, struct file *fp)302{303 return single_open(fp, mhi_debugfs_events_show, inode->i_private);304}305 306static int mhi_debugfs_channels_open(struct inode *inode, struct file *fp)307{308 return single_open(fp, mhi_debugfs_channels_show, inode->i_private);309}310 311static int mhi_debugfs_devices_open(struct inode *inode, struct file *fp)312{313 return single_open(fp, mhi_debugfs_devices_show, inode->i_private);314}315 316static int mhi_debugfs_regdump_open(struct inode *inode, struct file *fp)317{318 return single_open(fp, mhi_debugfs_regdump_show, inode->i_private);319}320 321static int mhi_debugfs_device_wake_open(struct inode *inode, struct file *fp)322{323 return single_open(fp, mhi_debugfs_device_wake_show, inode->i_private);324}325 326static int mhi_debugfs_timeout_ms_open(struct inode *inode, struct file *fp)327{328 return single_open(fp, mhi_debugfs_timeout_ms_show, inode->i_private);329}330 331static const struct file_operations debugfs_states_fops = {332 .open = mhi_debugfs_states_open,333 .release = single_release,334 .read = seq_read,335};336 337static const struct file_operations debugfs_events_fops = {338 .open = mhi_debugfs_events_open,339 .release = single_release,340 .read = seq_read,341};342 343static const struct file_operations debugfs_channels_fops = {344 .open = mhi_debugfs_channels_open,345 .release = single_release,346 .read = seq_read,347};348 349static const struct file_operations debugfs_devices_fops = {350 .open = mhi_debugfs_devices_open,351 .release = single_release,352 .read = seq_read,353};354 355static const struct file_operations debugfs_regdump_fops = {356 .open = mhi_debugfs_regdump_open,357 .release = single_release,358 .read = seq_read,359};360 361static const struct file_operations debugfs_device_wake_fops = {362 .open = mhi_debugfs_device_wake_open,363 .write = mhi_debugfs_device_wake_write,364 .release = single_release,365 .read = seq_read,366};367 368static const struct file_operations debugfs_timeout_ms_fops = {369 .open = mhi_debugfs_timeout_ms_open,370 .write = mhi_debugfs_timeout_ms_write,371 .release = single_release,372 .read = seq_read,373};374 375static struct dentry *mhi_debugfs_root;376 377void mhi_create_debugfs(struct mhi_controller *mhi_cntrl)378{379 mhi_cntrl->debugfs_dentry =380 debugfs_create_dir(dev_name(&mhi_cntrl->mhi_dev->dev),381 mhi_debugfs_root);382 383 debugfs_create_file("states", 0444, mhi_cntrl->debugfs_dentry,384 mhi_cntrl, &debugfs_states_fops);385 debugfs_create_file("events", 0444, mhi_cntrl->debugfs_dentry,386 mhi_cntrl, &debugfs_events_fops);387 debugfs_create_file("channels", 0444, mhi_cntrl->debugfs_dentry,388 mhi_cntrl, &debugfs_channels_fops);389 debugfs_create_file("devices", 0444, mhi_cntrl->debugfs_dentry,390 mhi_cntrl, &debugfs_devices_fops);391 debugfs_create_file("regdump", 0444, mhi_cntrl->debugfs_dentry,392 mhi_cntrl, &debugfs_regdump_fops);393 debugfs_create_file("device_wake", 0644, mhi_cntrl->debugfs_dentry,394 mhi_cntrl, &debugfs_device_wake_fops);395 debugfs_create_file("timeout_ms", 0644, mhi_cntrl->debugfs_dentry,396 mhi_cntrl, &debugfs_timeout_ms_fops);397}398 399void mhi_destroy_debugfs(struct mhi_controller *mhi_cntrl)400{401 debugfs_remove_recursive(mhi_cntrl->debugfs_dentry);402 mhi_cntrl->debugfs_dentry = NULL;403}404 405void mhi_debugfs_init(void)406{407 mhi_debugfs_root = debugfs_create_dir(mhi_bus_type.name, NULL);408}409 410void mhi_debugfs_exit(void)411{412 debugfs_remove_recursive(mhi_debugfs_root);413}414