linux-loongson/drivers/usb/mtu3/mtu3_debugfs.c
Linus Torvalds 2ab002c755 Driver core and debugfs updates
Here is the big set of driver core and debugfs updates for 6.14-rc1.
 It's coming late in the merge cycle as there are a number of merge
 conflicts with your tree now, and I wanted to make sure they were
 working properly.  To resolve them, look in linux-next, and I will send
 the "fixup" patch as a response to the pull request.
 
 Included in here is a bunch of driver core, PCI, OF, and platform rust
 bindings (all acked by the different subsystem maintainers), hence the
 merge conflict with the rust tree, and some driver core api updates to
 mark things as const, which will also require some fixups due to new
 stuff coming in through other trees in this merge window.
 
 There are also a bunch of debugfs updates from Al, and there is at least
 one user that does have a regression with these, but Al is working on
 tracking down the fix for it.  In my use (and everyone else's linux-next
 use), it does not seem like a big issue at the moment.
 
 Here's a short list of the things in here:
   - driver core bindings for PCI, platform, OF, and some i/o functions.
     We are almost at the "write a real driver in rust" stage now,
     depending on what you want to do.
   - misc device rust bindings and a sample driver to show how to use
     them
   - debugfs cleanups in the fs as well as the users of the fs api for
     places where drivers got it wrong or were unnecessarily doing things
     in complex ways.
   - driver core const work, making more of the api take const * for
     different parameters to make the rust bindings easier overall.
   - other small fixes and updates
 
 All of these have been in linux-next with all of the aforementioned
 merge conflicts, and the one debugfs issue, which looks to be resolved
 "soon".
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 
 iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCZ5koPA8cZ3JlZ0Brcm9h
 aC5jb20ACgkQMUfUDdst+ymFHACfT5acDKf2Bov2Lc/5u3vBW/R6ChsAnj+LmgVI
 hcDSPodj4szR40RRnzBd
 =u5Ey
 -----END PGP SIGNATURE-----

Merge tag 'driver-core-6.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core

Pull driver core and debugfs updates from Greg KH:
 "Here is the big set of driver core and debugfs updates for 6.14-rc1.

  Included in here is a bunch of driver core, PCI, OF, and platform rust
  bindings (all acked by the different subsystem maintainers), hence the
  merge conflict with the rust tree, and some driver core api updates to
  mark things as const, which will also require some fixups due to new
  stuff coming in through other trees in this merge window.

  There are also a bunch of debugfs updates from Al, and there is at
  least one user that does have a regression with these, but Al is
  working on tracking down the fix for it. In my use (and everyone
  else's linux-next use), it does not seem like a big issue at the
  moment.

  Here's a short list of the things in here:

   - driver core rust bindings for PCI, platform, OF, and some i/o
     functions.

     We are almost at the "write a real driver in rust" stage now,
     depending on what you want to do.

   - misc device rust bindings and a sample driver to show how to use
     them

   - debugfs cleanups in the fs as well as the users of the fs api for
     places where drivers got it wrong or were unnecessarily doing
     things in complex ways.

   - driver core const work, making more of the api take const * for
     different parameters to make the rust bindings easier overall.

   - other small fixes and updates

  All of these have been in linux-next with all of the aforementioned
  merge conflicts, and the one debugfs issue, which looks to be resolved
  "soon""

* tag 'driver-core-6.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core: (95 commits)
  rust: device: Use as_char_ptr() to avoid explicit cast
  rust: device: Replace CString with CStr in property_present()
  devcoredump: Constify 'struct bin_attribute'
  devcoredump: Define 'struct bin_attribute' through macro
  rust: device: Add property_present()
  saner replacement for debugfs_rename()
  orangefs-debugfs: don't mess with ->d_name
  octeontx2: don't mess with ->d_parent or ->d_parent->d_name
  arm_scmi: don't mess with ->d_parent->d_name
  slub: don't mess with ->d_name
  sof-client-ipc-flood-test: don't mess with ->d_name
  qat: don't mess with ->d_name
  xhci: don't mess with ->d_iname
  mtu3: don't mess wiht ->d_iname
  greybus/camera - stop messing with ->d_iname
  mediatek: stop messing with ->d_iname
  netdevsim: don't embed file_operations into your structs
  b43legacy: make use of debugfs_get_aux()
  b43: stop embedding struct file_operations into their objects
  carl9170: stop embedding file_operations into their objects
  ...
2025-01-28 12:25:12 -08:00

516 lines
13 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* mtu3_debugfs.c - debugfs interface
*
* Copyright (C) 2019 MediaTek Inc.
*
* Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
*/
#include <linux/string_choices.h>
#include <linux/uaccess.h>
#include "mtu3.h"
#include "mtu3_dr.h"
#include "mtu3_debug.h"
#define dump_register(nm) \
{ \
.name = __stringify(nm), \
.offset = U3D_ ##nm, \
}
#define dump_prb_reg(nm, os) \
{ \
.name = nm, \
.offset = os, \
}
static const struct debugfs_reg32 mtu3_ippc_regs[] = {
dump_register(SSUSB_IP_PW_CTRL0),
dump_register(SSUSB_IP_PW_CTRL1),
dump_register(SSUSB_IP_PW_CTRL2),
dump_register(SSUSB_IP_PW_CTRL3),
dump_register(SSUSB_IP_PW_STS1),
dump_register(SSUSB_OTG_STS),
dump_register(SSUSB_IP_XHCI_CAP),
dump_register(SSUSB_IP_DEV_CAP),
dump_register(SSUSB_U3_CTRL_0P),
dump_register(SSUSB_U2_CTRL_0P),
dump_register(SSUSB_HW_ID),
dump_register(SSUSB_HW_SUB_ID),
dump_register(SSUSB_IP_SPARE0),
};
static const struct debugfs_reg32 mtu3_dev_regs[] = {
dump_register(LV1ISR),
dump_register(LV1IER),
dump_register(EPISR),
dump_register(EPIER),
dump_register(EP0CSR),
dump_register(RXCOUNT0),
dump_register(QISAR0),
dump_register(QIER0),
dump_register(QISAR1),
dump_register(QIER1),
dump_register(CAP_EPNTXFFSZ),
dump_register(CAP_EPNRXFFSZ),
dump_register(CAP_EPINFO),
dump_register(MISC_CTRL),
};
static const struct debugfs_reg32 mtu3_csr_regs[] = {
dump_register(DEVICE_CONF),
dump_register(DEV_LINK_INTR_ENABLE),
dump_register(DEV_LINK_INTR),
dump_register(LTSSM_CTRL),
dump_register(USB3_CONFIG),
dump_register(LINK_STATE_MACHINE),
dump_register(LTSSM_INTR_ENABLE),
dump_register(LTSSM_INTR),
dump_register(U3U2_SWITCH_CTRL),
dump_register(POWER_MANAGEMENT),
dump_register(DEVICE_CONTROL),
dump_register(COMMON_USB_INTR_ENABLE),
dump_register(COMMON_USB_INTR),
dump_register(USB20_MISC_CONTROL),
dump_register(USB20_OPSTATE),
};
static int mtu3_link_state_show(struct seq_file *sf, void *unused)
{
struct mtu3 *mtu = sf->private;
void __iomem *mbase = mtu->mac_base;
seq_printf(sf, "opstate: %#x, ltssm: %#x\n",
mtu3_readl(mbase, U3D_USB20_OPSTATE),
LTSSM_STATE(mtu3_readl(mbase, U3D_LINK_STATE_MACHINE)));
return 0;
}
static int mtu3_ep_used_show(struct seq_file *sf, void *unused)
{
struct mtu3 *mtu = sf->private;
struct mtu3_ep *mep;
unsigned long flags;
int used = 0;
int i;
spin_lock_irqsave(&mtu->lock, flags);
for (i = 0; i < mtu->num_eps; i++) {
mep = mtu->in_eps + i;
if (mep->flags & MTU3_EP_ENABLED) {
seq_printf(sf, "%s - type: %s\n", mep->name, usb_ep_type_string(mep->type));
used++;
}
mep = mtu->out_eps + i;
if (mep->flags & MTU3_EP_ENABLED) {
seq_printf(sf, "%s - type: %s\n", mep->name, usb_ep_type_string(mep->type));
used++;
}
}
seq_printf(sf, "total used: %d eps\n", used);
spin_unlock_irqrestore(&mtu->lock, flags);
return 0;
}
DEFINE_SHOW_ATTRIBUTE(mtu3_link_state);
DEFINE_SHOW_ATTRIBUTE(mtu3_ep_used);
static void mtu3_debugfs_regset(struct mtu3 *mtu, void __iomem *base,
const struct debugfs_reg32 *regs, size_t nregs,
const char *name, struct dentry *parent)
{
struct debugfs_regset32 *regset;
struct mtu3_regset *mregs;
mregs = devm_kzalloc(mtu->dev, sizeof(*mregs), GFP_KERNEL);
if (!mregs)
return;
sprintf(mregs->name, "%s", name);
regset = &mregs->regset;
regset->regs = regs;
regset->nregs = nregs;
regset->base = base;
debugfs_create_regset32(mregs->name, 0444, parent, regset);
}
static void mtu3_debugfs_ep_regset(struct mtu3 *mtu, struct mtu3_ep *mep,
struct dentry *parent)
{
struct debugfs_reg32 *regs;
int epnum = mep->epnum;
int in = mep->is_in;
regs = devm_kcalloc(mtu->dev, 7, sizeof(*regs), GFP_KERNEL);
if (!regs)
return;
regs[0].name = in ? "TCR0" : "RCR0";
regs[0].offset = in ? MU3D_EP_TXCR0(epnum) : MU3D_EP_RXCR0(epnum);
regs[1].name = in ? "TCR1" : "RCR1";
regs[1].offset = in ? MU3D_EP_TXCR1(epnum) : MU3D_EP_RXCR1(epnum);
regs[2].name = in ? "TCR2" : "RCR2";
regs[2].offset = in ? MU3D_EP_TXCR2(epnum) : MU3D_EP_RXCR2(epnum);
regs[3].name = in ? "TQHIAR" : "RQHIAR";
regs[3].offset = in ? USB_QMU_TQHIAR(epnum) : USB_QMU_RQHIAR(epnum);
regs[4].name = in ? "TQCSR" : "RQCSR";
regs[4].offset = in ? USB_QMU_TQCSR(epnum) : USB_QMU_RQCSR(epnum);
regs[5].name = in ? "TQSAR" : "RQSAR";
regs[5].offset = in ? USB_QMU_TQSAR(epnum) : USB_QMU_RQSAR(epnum);
regs[6].name = in ? "TQCPR" : "RQCPR";
regs[6].offset = in ? USB_QMU_TQCPR(epnum) : USB_QMU_RQCPR(epnum);
mtu3_debugfs_regset(mtu, mtu->mac_base, regs, 7, "ep-regs", parent);
}
static int mtu3_ep_info_show(struct seq_file *sf, void *unused)
{
struct mtu3_ep *mep = sf->private;
struct mtu3 *mtu = mep->mtu;
unsigned long flags;
spin_lock_irqsave(&mtu->lock, flags);
seq_printf(sf, "ep - type:%s, maxp:%d, slot:%d, flags:%x\n",
usb_ep_type_string(mep->type), mep->maxp, mep->slot, mep->flags);
spin_unlock_irqrestore(&mtu->lock, flags);
return 0;
}
static int mtu3_fifo_show(struct seq_file *sf, void *unused)
{
struct mtu3_ep *mep = sf->private;
struct mtu3 *mtu = mep->mtu;
unsigned long flags;
spin_lock_irqsave(&mtu->lock, flags);
seq_printf(sf, "fifo - seg_size:%d, addr:%d, size:%d\n",
mep->fifo_seg_size, mep->fifo_addr, mep->fifo_size);
spin_unlock_irqrestore(&mtu->lock, flags);
return 0;
}
static int mtu3_qmu_ring_show(struct seq_file *sf, void *unused)
{
struct mtu3_ep *mep = sf->private;
struct mtu3 *mtu = mep->mtu;
struct mtu3_gpd_ring *ring;
unsigned long flags;
ring = &mep->gpd_ring;
spin_lock_irqsave(&mtu->lock, flags);
seq_printf(sf,
"qmu-ring - dma:%pad, start:%p, end:%p, enq:%p, dep:%p\n",
&ring->dma, ring->start, ring->end,
ring->enqueue, ring->dequeue);
spin_unlock_irqrestore(&mtu->lock, flags);
return 0;
}
static int mtu3_qmu_gpd_show(struct seq_file *sf, void *unused)
{
struct mtu3_ep *mep = sf->private;
struct mtu3 *mtu = mep->mtu;
struct mtu3_gpd_ring *ring;
struct qmu_gpd *gpd;
dma_addr_t dma;
unsigned long flags;
int i;
spin_lock_irqsave(&mtu->lock, flags);
ring = &mep->gpd_ring;
gpd = ring->start;
if (!gpd || !(mep->flags & MTU3_EP_ENABLED)) {
seq_puts(sf, "empty!\n");
goto out;
}
for (i = 0; i < MAX_GPD_NUM; i++, gpd++) {
dma = ring->dma + i * sizeof(*gpd);
seq_printf(sf, "gpd.%03d -> %pad, %p: %08x %08x %08x %08x\n",
i, &dma, gpd, gpd->dw0_info, gpd->next_gpd,
gpd->buffer, gpd->dw3_info);
}
out:
spin_unlock_irqrestore(&mtu->lock, flags);
return 0;
}
static const struct mtu3_file_map mtu3_ep_files[] = {
{"ep-info", mtu3_ep_info_show, },
{"fifo", mtu3_fifo_show, },
{"qmu-ring", mtu3_qmu_ring_show, },
{"qmu-gpd", mtu3_qmu_gpd_show, },
};
static int mtu3_ep_open(struct inode *inode, struct file *file)
{
const struct mtu3_file_map *f_map = debugfs_get_aux(file);
return single_open(file, f_map->show, inode->i_private);
}
static const struct file_operations mtu3_ep_fops = {
.open = mtu3_ep_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static const struct debugfs_reg32 mtu3_prb_regs[] = {
dump_prb_reg("enable", U3D_SSUSB_PRB_CTRL0),
dump_prb_reg("byte-sell", U3D_SSUSB_PRB_CTRL1),
dump_prb_reg("byte-selh", U3D_SSUSB_PRB_CTRL2),
dump_prb_reg("module-sel", U3D_SSUSB_PRB_CTRL3),
dump_prb_reg("sw-out", U3D_SSUSB_PRB_CTRL4),
dump_prb_reg("data", U3D_SSUSB_PRB_CTRL5),
};
static int mtu3_probe_show(struct seq_file *sf, void *unused)
{
struct mtu3 *mtu = sf->private;
const struct debugfs_reg32 *regs = debugfs_get_aux(sf->file);
seq_printf(sf, "0x%04x - 0x%08x\n", (u32)regs->offset,
mtu3_readl(mtu->ippc_base, (u32)regs->offset));
return 0;
}
static int mtu3_probe_open(struct inode *inode, struct file *file)
{
return single_open(file, mtu3_probe_show, inode->i_private);
}
static ssize_t mtu3_probe_write(struct file *file, const char __user *ubuf,
size_t count, loff_t *ppos)
{
struct seq_file *sf = file->private_data;
struct mtu3 *mtu = sf->private;
const struct debugfs_reg32 *regs = debugfs_get_aux(file);
char buf[32];
u32 val;
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
return -EFAULT;
if (kstrtou32(buf, 0, &val))
return -EINVAL;
mtu3_writel(mtu->ippc_base, (u32)regs->offset, val);
return count;
}
static const struct file_operations mtu3_probe_fops = {
.open = mtu3_probe_open,
.write = mtu3_probe_write,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static void mtu3_debugfs_create_prb_files(struct mtu3 *mtu)
{
struct ssusb_mtk *ssusb = mtu->ssusb;
const struct debugfs_reg32 *regs;
struct dentry *dir_prb;
int i;
dir_prb = debugfs_create_dir("probe", ssusb->dbgfs_root);
for (i = 0; i < ARRAY_SIZE(mtu3_prb_regs); i++) {
regs = &mtu3_prb_regs[i];
debugfs_create_file_aux(regs->name, 0644, dir_prb,
mtu, regs, &mtu3_probe_fops);
}
mtu3_debugfs_regset(mtu, mtu->ippc_base, mtu3_prb_regs,
ARRAY_SIZE(mtu3_prb_regs), "regs", dir_prb);
}
static void mtu3_debugfs_create_ep_dir(struct mtu3_ep *mep,
struct dentry *parent)
{
const struct mtu3_file_map *files;
struct dentry *dir_ep;
int i;
dir_ep = debugfs_create_dir(mep->name, parent);
mtu3_debugfs_ep_regset(mep->mtu, mep, dir_ep);
for (i = 0; i < ARRAY_SIZE(mtu3_ep_files); i++) {
files = &mtu3_ep_files[i];
debugfs_create_file_aux(files->name, 0444, dir_ep,
mep, files, &mtu3_ep_fops);
}
}
static void mtu3_debugfs_create_ep_dirs(struct mtu3 *mtu)
{
struct ssusb_mtk *ssusb = mtu->ssusb;
struct dentry *dir_eps;
int i;
dir_eps = debugfs_create_dir("eps", ssusb->dbgfs_root);
for (i = 1; i < mtu->num_eps; i++) {
mtu3_debugfs_create_ep_dir(mtu->in_eps + i, dir_eps);
mtu3_debugfs_create_ep_dir(mtu->out_eps + i, dir_eps);
}
}
void ssusb_dev_debugfs_init(struct ssusb_mtk *ssusb)
{
struct mtu3 *mtu = ssusb->u3d;
struct dentry *dir_regs;
dir_regs = debugfs_create_dir("regs", ssusb->dbgfs_root);
mtu3_debugfs_regset(mtu, mtu->ippc_base,
mtu3_ippc_regs, ARRAY_SIZE(mtu3_ippc_regs),
"reg-ippc", dir_regs);
mtu3_debugfs_regset(mtu, mtu->mac_base,
mtu3_dev_regs, ARRAY_SIZE(mtu3_dev_regs),
"reg-dev", dir_regs);
mtu3_debugfs_regset(mtu, mtu->mac_base,
mtu3_csr_regs, ARRAY_SIZE(mtu3_csr_regs),
"reg-csr", dir_regs);
mtu3_debugfs_create_ep_dirs(mtu);
mtu3_debugfs_create_prb_files(mtu);
debugfs_create_file("link-state", 0444, ssusb->dbgfs_root,
mtu, &mtu3_link_state_fops);
debugfs_create_file("ep-used", 0444, ssusb->dbgfs_root,
mtu, &mtu3_ep_used_fops);
}
static int ssusb_mode_show(struct seq_file *sf, void *unused)
{
struct ssusb_mtk *ssusb = sf->private;
seq_printf(sf, "current mode: %s(%s drd)\n(echo device/host)\n",
ssusb->is_host ? "host" : "device",
ssusb->otg_switch.manual_drd_enabled ? "manual" : "auto");
return 0;
}
static int ssusb_mode_open(struct inode *inode, struct file *file)
{
return single_open(file, ssusb_mode_show, inode->i_private);
}
static ssize_t ssusb_mode_write(struct file *file, const char __user *ubuf,
size_t count, loff_t *ppos)
{
struct seq_file *sf = file->private_data;
struct ssusb_mtk *ssusb = sf->private;
char buf[16];
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
return -EFAULT;
if (!strncmp(buf, "host", 4) && !ssusb->is_host) {
ssusb_mode_switch(ssusb, 1);
} else if (!strncmp(buf, "device", 6) && ssusb->is_host) {
ssusb_mode_switch(ssusb, 0);
} else {
dev_err(ssusb->dev, "wrong or duplicated setting\n");
return -EINVAL;
}
return count;
}
static const struct file_operations ssusb_mode_fops = {
.open = ssusb_mode_open,
.write = ssusb_mode_write,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int ssusb_vbus_show(struct seq_file *sf, void *unused)
{
struct ssusb_mtk *ssusb = sf->private;
struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
seq_printf(sf, "vbus state: %s\n(echo on/off)\n",
str_on_off(regulator_is_enabled(otg_sx->vbus)));
return 0;
}
static int ssusb_vbus_open(struct inode *inode, struct file *file)
{
return single_open(file, ssusb_vbus_show, inode->i_private);
}
static ssize_t ssusb_vbus_write(struct file *file, const char __user *ubuf,
size_t count, loff_t *ppos)
{
struct seq_file *sf = file->private_data;
struct ssusb_mtk *ssusb = sf->private;
struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
char buf[16];
bool enable;
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
return -EFAULT;
if (kstrtobool(buf, &enable)) {
dev_err(ssusb->dev, "wrong setting\n");
return -EINVAL;
}
ssusb_set_vbus(otg_sx, enable);
return count;
}
static const struct file_operations ssusb_vbus_fops = {
.open = ssusb_vbus_open,
.write = ssusb_vbus_write,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
void ssusb_dr_debugfs_init(struct ssusb_mtk *ssusb)
{
struct dentry *root = ssusb->dbgfs_root;
debugfs_create_file("mode", 0644, root, ssusb, &ssusb_mode_fops);
debugfs_create_file("vbus", 0644, root, ssusb, &ssusb_vbus_fops);
}
void ssusb_debugfs_create_root(struct ssusb_mtk *ssusb)
{
ssusb->dbgfs_root =
debugfs_create_dir(dev_name(ssusb->dev), usb_debug_root);
}
void ssusb_debugfs_remove_root(struct ssusb_mtk *ssusb)
{
debugfs_remove_recursive(ssusb->dbgfs_root);
ssusb->dbgfs_root = NULL;
}