mtu3_debugfs.c (13796B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * mtu3_debugfs.c - debugfs interface 4 * 5 * Copyright (C) 2019 MediaTek Inc. 6 * 7 * Author: Chunfeng Yun <chunfeng.yun@mediatek.com> 8 */ 9 10#include <linux/uaccess.h> 11 12#include "mtu3.h" 13#include "mtu3_dr.h" 14#include "mtu3_debug.h" 15 16#define dump_register(nm) \ 17{ \ 18 .name = __stringify(nm), \ 19 .offset = U3D_ ##nm, \ 20} 21 22#define dump_prb_reg(nm, os) \ 23{ \ 24 .name = nm, \ 25 .offset = os, \ 26} 27 28static const struct debugfs_reg32 mtu3_ippc_regs[] = { 29 dump_register(SSUSB_IP_PW_CTRL0), 30 dump_register(SSUSB_IP_PW_CTRL1), 31 dump_register(SSUSB_IP_PW_CTRL2), 32 dump_register(SSUSB_IP_PW_CTRL3), 33 dump_register(SSUSB_IP_PW_STS1), 34 dump_register(SSUSB_OTG_STS), 35 dump_register(SSUSB_IP_XHCI_CAP), 36 dump_register(SSUSB_IP_DEV_CAP), 37 dump_register(SSUSB_U3_CTRL_0P), 38 dump_register(SSUSB_U2_CTRL_0P), 39 dump_register(SSUSB_HW_ID), 40 dump_register(SSUSB_HW_SUB_ID), 41 dump_register(SSUSB_IP_SPARE0), 42}; 43 44static const struct debugfs_reg32 mtu3_dev_regs[] = { 45 dump_register(LV1ISR), 46 dump_register(LV1IER), 47 dump_register(EPISR), 48 dump_register(EPIER), 49 dump_register(EP0CSR), 50 dump_register(RXCOUNT0), 51 dump_register(QISAR0), 52 dump_register(QIER0), 53 dump_register(QISAR1), 54 dump_register(QIER1), 55 dump_register(CAP_EPNTXFFSZ), 56 dump_register(CAP_EPNRXFFSZ), 57 dump_register(CAP_EPINFO), 58 dump_register(MISC_CTRL), 59}; 60 61static const struct debugfs_reg32 mtu3_csr_regs[] = { 62 dump_register(DEVICE_CONF), 63 dump_register(DEV_LINK_INTR_ENABLE), 64 dump_register(DEV_LINK_INTR), 65 dump_register(LTSSM_CTRL), 66 dump_register(USB3_CONFIG), 67 dump_register(LINK_STATE_MACHINE), 68 dump_register(LTSSM_INTR_ENABLE), 69 dump_register(LTSSM_INTR), 70 dump_register(U3U2_SWITCH_CTRL), 71 dump_register(POWER_MANAGEMENT), 72 dump_register(DEVICE_CONTROL), 73 dump_register(COMMON_USB_INTR_ENABLE), 74 dump_register(COMMON_USB_INTR), 75 dump_register(USB20_MISC_CONTROL), 76 dump_register(USB20_OPSTATE), 77}; 78 79static int mtu3_link_state_show(struct seq_file *sf, void *unused) 80{ 81 struct mtu3 *mtu = sf->private; 82 void __iomem *mbase = mtu->mac_base; 83 84 seq_printf(sf, "opstate: %#x, ltssm: %#x\n", 85 mtu3_readl(mbase, U3D_USB20_OPSTATE), 86 LTSSM_STATE(mtu3_readl(mbase, U3D_LINK_STATE_MACHINE))); 87 88 return 0; 89} 90 91static int mtu3_ep_used_show(struct seq_file *sf, void *unused) 92{ 93 struct mtu3 *mtu = sf->private; 94 struct mtu3_ep *mep; 95 unsigned long flags; 96 int used = 0; 97 int i; 98 99 spin_lock_irqsave(&mtu->lock, flags); 100 101 for (i = 0; i < mtu->num_eps; i++) { 102 mep = mtu->in_eps + i; 103 if (mep->flags & MTU3_EP_ENABLED) { 104 seq_printf(sf, "%s - type: %d\n", mep->name, mep->type); 105 used++; 106 } 107 108 mep = mtu->out_eps + i; 109 if (mep->flags & MTU3_EP_ENABLED) { 110 seq_printf(sf, "%s - type: %d\n", mep->name, mep->type); 111 used++; 112 } 113 } 114 seq_printf(sf, "total used: %d eps\n", used); 115 116 spin_unlock_irqrestore(&mtu->lock, flags); 117 118 return 0; 119} 120 121DEFINE_SHOW_ATTRIBUTE(mtu3_link_state); 122DEFINE_SHOW_ATTRIBUTE(mtu3_ep_used); 123 124static void mtu3_debugfs_regset(struct mtu3 *mtu, void __iomem *base, 125 const struct debugfs_reg32 *regs, size_t nregs, 126 const char *name, struct dentry *parent) 127{ 128 struct debugfs_regset32 *regset; 129 struct mtu3_regset *mregs; 130 131 mregs = devm_kzalloc(mtu->dev, sizeof(*mregs), GFP_KERNEL); 132 if (!mregs) 133 return; 134 135 sprintf(mregs->name, "%s", name); 136 regset = &mregs->regset; 137 regset->regs = regs; 138 regset->nregs = nregs; 139 regset->base = base; 140 141 debugfs_create_regset32(mregs->name, 0444, parent, regset); 142} 143 144static void mtu3_debugfs_ep_regset(struct mtu3 *mtu, struct mtu3_ep *mep, 145 struct dentry *parent) 146{ 147 struct debugfs_reg32 *regs; 148 int epnum = mep->epnum; 149 int in = mep->is_in; 150 151 regs = devm_kcalloc(mtu->dev, 7, sizeof(*regs), GFP_KERNEL); 152 if (!regs) 153 return; 154 155 regs[0].name = in ? "TCR0" : "RCR0"; 156 regs[0].offset = in ? MU3D_EP_TXCR0(epnum) : MU3D_EP_RXCR0(epnum); 157 regs[1].name = in ? "TCR1" : "RCR1"; 158 regs[1].offset = in ? MU3D_EP_TXCR1(epnum) : MU3D_EP_RXCR1(epnum); 159 regs[2].name = in ? "TCR2" : "RCR2"; 160 regs[2].offset = in ? MU3D_EP_TXCR2(epnum) : MU3D_EP_RXCR2(epnum); 161 regs[3].name = in ? "TQHIAR" : "RQHIAR"; 162 regs[3].offset = in ? USB_QMU_TQHIAR(epnum) : USB_QMU_RQHIAR(epnum); 163 regs[4].name = in ? "TQCSR" : "RQCSR"; 164 regs[4].offset = in ? USB_QMU_TQCSR(epnum) : USB_QMU_RQCSR(epnum); 165 regs[5].name = in ? "TQSAR" : "RQSAR"; 166 regs[5].offset = in ? USB_QMU_TQSAR(epnum) : USB_QMU_RQSAR(epnum); 167 regs[6].name = in ? "TQCPR" : "RQCPR"; 168 regs[6].offset = in ? USB_QMU_TQCPR(epnum) : USB_QMU_RQCPR(epnum); 169 170 mtu3_debugfs_regset(mtu, mtu->mac_base, regs, 7, "ep-regs", parent); 171} 172 173static int mtu3_ep_info_show(struct seq_file *sf, void *unused) 174{ 175 struct mtu3_ep *mep = sf->private; 176 struct mtu3 *mtu = mep->mtu; 177 unsigned long flags; 178 179 spin_lock_irqsave(&mtu->lock, flags); 180 seq_printf(sf, "ep - type:%d, maxp:%d, slot:%d, flags:%x\n", 181 mep->type, mep->maxp, mep->slot, mep->flags); 182 spin_unlock_irqrestore(&mtu->lock, flags); 183 184 return 0; 185} 186 187static int mtu3_fifo_show(struct seq_file *sf, void *unused) 188{ 189 struct mtu3_ep *mep = sf->private; 190 struct mtu3 *mtu = mep->mtu; 191 unsigned long flags; 192 193 spin_lock_irqsave(&mtu->lock, flags); 194 seq_printf(sf, "fifo - seg_size:%d, addr:%d, size:%d\n", 195 mep->fifo_seg_size, mep->fifo_addr, mep->fifo_size); 196 spin_unlock_irqrestore(&mtu->lock, flags); 197 198 return 0; 199} 200 201static int mtu3_qmu_ring_show(struct seq_file *sf, void *unused) 202{ 203 struct mtu3_ep *mep = sf->private; 204 struct mtu3 *mtu = mep->mtu; 205 struct mtu3_gpd_ring *ring; 206 unsigned long flags; 207 208 ring = &mep->gpd_ring; 209 spin_lock_irqsave(&mtu->lock, flags); 210 seq_printf(sf, 211 "qmu-ring - dma:%pad, start:%p, end:%p, enq:%p, dep:%p\n", 212 &ring->dma, ring->start, ring->end, 213 ring->enqueue, ring->dequeue); 214 spin_unlock_irqrestore(&mtu->lock, flags); 215 216 return 0; 217} 218 219static int mtu3_qmu_gpd_show(struct seq_file *sf, void *unused) 220{ 221 struct mtu3_ep *mep = sf->private; 222 struct mtu3 *mtu = mep->mtu; 223 struct mtu3_gpd_ring *ring; 224 struct qmu_gpd *gpd; 225 dma_addr_t dma; 226 unsigned long flags; 227 int i; 228 229 spin_lock_irqsave(&mtu->lock, flags); 230 ring = &mep->gpd_ring; 231 gpd = ring->start; 232 if (!gpd || !(mep->flags & MTU3_EP_ENABLED)) { 233 seq_puts(sf, "empty!\n"); 234 goto out; 235 } 236 237 for (i = 0; i < MAX_GPD_NUM; i++, gpd++) { 238 dma = ring->dma + i * sizeof(*gpd); 239 seq_printf(sf, "gpd.%03d -> %pad, %p: %08x %08x %08x %08x\n", 240 i, &dma, gpd, gpd->dw0_info, gpd->next_gpd, 241 gpd->buffer, gpd->dw3_info); 242 } 243 244out: 245 spin_unlock_irqrestore(&mtu->lock, flags); 246 247 return 0; 248} 249 250static const struct mtu3_file_map mtu3_ep_files[] = { 251 {"ep-info", mtu3_ep_info_show, }, 252 {"fifo", mtu3_fifo_show, }, 253 {"qmu-ring", mtu3_qmu_ring_show, }, 254 {"qmu-gpd", mtu3_qmu_gpd_show, }, 255}; 256 257static int mtu3_ep_open(struct inode *inode, struct file *file) 258{ 259 const char *file_name = file_dentry(file)->d_iname; 260 const struct mtu3_file_map *f_map; 261 int i; 262 263 for (i = 0; i < ARRAY_SIZE(mtu3_ep_files); i++) { 264 f_map = &mtu3_ep_files[i]; 265 266 if (strcmp(f_map->name, file_name) == 0) 267 break; 268 } 269 270 return single_open(file, f_map->show, inode->i_private); 271} 272 273static const struct file_operations mtu3_ep_fops = { 274 .open = mtu3_ep_open, 275 .read = seq_read, 276 .llseek = seq_lseek, 277 .release = single_release, 278}; 279 280static const struct debugfs_reg32 mtu3_prb_regs[] = { 281 dump_prb_reg("enable", U3D_SSUSB_PRB_CTRL0), 282 dump_prb_reg("byte-sell", U3D_SSUSB_PRB_CTRL1), 283 dump_prb_reg("byte-selh", U3D_SSUSB_PRB_CTRL2), 284 dump_prb_reg("module-sel", U3D_SSUSB_PRB_CTRL3), 285 dump_prb_reg("sw-out", U3D_SSUSB_PRB_CTRL4), 286 dump_prb_reg("data", U3D_SSUSB_PRB_CTRL5), 287}; 288 289static int mtu3_probe_show(struct seq_file *sf, void *unused) 290{ 291 const char *file_name = file_dentry(sf->file)->d_iname; 292 struct mtu3 *mtu = sf->private; 293 const struct debugfs_reg32 *regs; 294 int i; 295 296 for (i = 0; i < ARRAY_SIZE(mtu3_prb_regs); i++) { 297 regs = &mtu3_prb_regs[i]; 298 299 if (strcmp(regs->name, file_name) == 0) 300 break; 301 } 302 303 seq_printf(sf, "0x%04x - 0x%08x\n", (u32)regs->offset, 304 mtu3_readl(mtu->ippc_base, (u32)regs->offset)); 305 306 return 0; 307} 308 309static int mtu3_probe_open(struct inode *inode, struct file *file) 310{ 311 return single_open(file, mtu3_probe_show, inode->i_private); 312} 313 314static ssize_t mtu3_probe_write(struct file *file, const char __user *ubuf, 315 size_t count, loff_t *ppos) 316{ 317 const char *file_name = file_dentry(file)->d_iname; 318 struct seq_file *sf = file->private_data; 319 struct mtu3 *mtu = sf->private; 320 const struct debugfs_reg32 *regs; 321 char buf[32]; 322 u32 val; 323 int i; 324 325 if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) 326 return -EFAULT; 327 328 if (kstrtou32(buf, 0, &val)) 329 return -EINVAL; 330 331 for (i = 0; i < ARRAY_SIZE(mtu3_prb_regs); i++) { 332 regs = &mtu3_prb_regs[i]; 333 334 if (strcmp(regs->name, file_name) == 0) 335 break; 336 } 337 mtu3_writel(mtu->ippc_base, (u32)regs->offset, val); 338 339 return count; 340} 341 342static const struct file_operations mtu3_probe_fops = { 343 .open = mtu3_probe_open, 344 .write = mtu3_probe_write, 345 .read = seq_read, 346 .llseek = seq_lseek, 347 .release = single_release, 348}; 349 350static void mtu3_debugfs_create_prb_files(struct mtu3 *mtu) 351{ 352 struct ssusb_mtk *ssusb = mtu->ssusb; 353 const struct debugfs_reg32 *regs; 354 struct dentry *dir_prb; 355 int i; 356 357 dir_prb = debugfs_create_dir("probe", ssusb->dbgfs_root); 358 359 for (i = 0; i < ARRAY_SIZE(mtu3_prb_regs); i++) { 360 regs = &mtu3_prb_regs[i]; 361 debugfs_create_file(regs->name, 0644, dir_prb, 362 mtu, &mtu3_probe_fops); 363 } 364 365 mtu3_debugfs_regset(mtu, mtu->ippc_base, mtu3_prb_regs, 366 ARRAY_SIZE(mtu3_prb_regs), "regs", dir_prb); 367} 368 369static void mtu3_debugfs_create_ep_dir(struct mtu3_ep *mep, 370 struct dentry *parent) 371{ 372 const struct mtu3_file_map *files; 373 struct dentry *dir_ep; 374 int i; 375 376 dir_ep = debugfs_create_dir(mep->name, parent); 377 mtu3_debugfs_ep_regset(mep->mtu, mep, dir_ep); 378 379 for (i = 0; i < ARRAY_SIZE(mtu3_ep_files); i++) { 380 files = &mtu3_ep_files[i]; 381 382 debugfs_create_file(files->name, 0444, dir_ep, 383 mep, &mtu3_ep_fops); 384 } 385} 386 387static void mtu3_debugfs_create_ep_dirs(struct mtu3 *mtu) 388{ 389 struct ssusb_mtk *ssusb = mtu->ssusb; 390 struct dentry *dir_eps; 391 int i; 392 393 dir_eps = debugfs_create_dir("eps", ssusb->dbgfs_root); 394 395 for (i = 1; i < mtu->num_eps; i++) { 396 mtu3_debugfs_create_ep_dir(mtu->in_eps + i, dir_eps); 397 mtu3_debugfs_create_ep_dir(mtu->out_eps + i, dir_eps); 398 } 399} 400 401void ssusb_dev_debugfs_init(struct ssusb_mtk *ssusb) 402{ 403 struct mtu3 *mtu = ssusb->u3d; 404 struct dentry *dir_regs; 405 406 dir_regs = debugfs_create_dir("regs", ssusb->dbgfs_root); 407 408 mtu3_debugfs_regset(mtu, mtu->ippc_base, 409 mtu3_ippc_regs, ARRAY_SIZE(mtu3_ippc_regs), 410 "reg-ippc", dir_regs); 411 412 mtu3_debugfs_regset(mtu, mtu->mac_base, 413 mtu3_dev_regs, ARRAY_SIZE(mtu3_dev_regs), 414 "reg-dev", dir_regs); 415 416 mtu3_debugfs_regset(mtu, mtu->mac_base, 417 mtu3_csr_regs, ARRAY_SIZE(mtu3_csr_regs), 418 "reg-csr", dir_regs); 419 420 mtu3_debugfs_create_ep_dirs(mtu); 421 422 mtu3_debugfs_create_prb_files(mtu); 423 424 debugfs_create_file("link-state", 0444, ssusb->dbgfs_root, 425 mtu, &mtu3_link_state_fops); 426 debugfs_create_file("ep-used", 0444, ssusb->dbgfs_root, 427 mtu, &mtu3_ep_used_fops); 428} 429 430static int ssusb_mode_show(struct seq_file *sf, void *unused) 431{ 432 struct ssusb_mtk *ssusb = sf->private; 433 434 seq_printf(sf, "current mode: %s(%s drd)\n(echo device/host)\n", 435 ssusb->is_host ? "host" : "device", 436 ssusb->otg_switch.manual_drd_enabled ? "manual" : "auto"); 437 438 return 0; 439} 440 441static int ssusb_mode_open(struct inode *inode, struct file *file) 442{ 443 return single_open(file, ssusb_mode_show, inode->i_private); 444} 445 446static ssize_t ssusb_mode_write(struct file *file, const char __user *ubuf, 447 size_t count, loff_t *ppos) 448{ 449 struct seq_file *sf = file->private_data; 450 struct ssusb_mtk *ssusb = sf->private; 451 char buf[16]; 452 453 if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) 454 return -EFAULT; 455 456 if (!strncmp(buf, "host", 4) && !ssusb->is_host) { 457 ssusb_mode_switch(ssusb, 1); 458 } else if (!strncmp(buf, "device", 6) && ssusb->is_host) { 459 ssusb_mode_switch(ssusb, 0); 460 } else { 461 dev_err(ssusb->dev, "wrong or duplicated setting\n"); 462 return -EINVAL; 463 } 464 465 return count; 466} 467 468static const struct file_operations ssusb_mode_fops = { 469 .open = ssusb_mode_open, 470 .write = ssusb_mode_write, 471 .read = seq_read, 472 .llseek = seq_lseek, 473 .release = single_release, 474}; 475 476static int ssusb_vbus_show(struct seq_file *sf, void *unused) 477{ 478 struct ssusb_mtk *ssusb = sf->private; 479 struct otg_switch_mtk *otg_sx = &ssusb->otg_switch; 480 481 seq_printf(sf, "vbus state: %s\n(echo on/off)\n", 482 regulator_is_enabled(otg_sx->vbus) ? "on" : "off"); 483 484 return 0; 485} 486 487static int ssusb_vbus_open(struct inode *inode, struct file *file) 488{ 489 return single_open(file, ssusb_vbus_show, inode->i_private); 490} 491 492static ssize_t ssusb_vbus_write(struct file *file, const char __user *ubuf, 493 size_t count, loff_t *ppos) 494{ 495 struct seq_file *sf = file->private_data; 496 struct ssusb_mtk *ssusb = sf->private; 497 struct otg_switch_mtk *otg_sx = &ssusb->otg_switch; 498 char buf[16]; 499 bool enable; 500 501 if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) 502 return -EFAULT; 503 504 if (kstrtobool(buf, &enable)) { 505 dev_err(ssusb->dev, "wrong setting\n"); 506 return -EINVAL; 507 } 508 509 ssusb_set_vbus(otg_sx, enable); 510 511 return count; 512} 513 514static const struct file_operations ssusb_vbus_fops = { 515 .open = ssusb_vbus_open, 516 .write = ssusb_vbus_write, 517 .read = seq_read, 518 .llseek = seq_lseek, 519 .release = single_release, 520}; 521 522void ssusb_dr_debugfs_init(struct ssusb_mtk *ssusb) 523{ 524 struct dentry *root = ssusb->dbgfs_root; 525 526 debugfs_create_file("mode", 0644, root, ssusb, &ssusb_mode_fops); 527 debugfs_create_file("vbus", 0644, root, ssusb, &ssusb_vbus_fops); 528} 529 530void ssusb_debugfs_create_root(struct ssusb_mtk *ssusb) 531{ 532 ssusb->dbgfs_root = 533 debugfs_create_dir(dev_name(ssusb->dev), usb_debug_root); 534} 535 536void ssusb_debugfs_remove_root(struct ssusb_mtk *ssusb) 537{ 538 debugfs_remove_recursive(ssusb->dbgfs_root); 539 ssusb->dbgfs_root = NULL; 540}