forked from mirrors/linux
usb: xhci: Add debugfs support for xHCI port bandwidth
In many projects, you need to obtain the available bandwidth of the xhci roothub port. Refer to xhci rev1_2 and use the TRB_GET_BW command to obtain it. hardware tested: 03:00.3 USB controller: Advanced Micro Devices, Inc. [AMD] Raven USB 3.1 (prog-if 30 [XHCI]) Subsystem: Huawei Technologies Co., Ltd. Raven USB 3.1 Flags: bus master, fast devsel, latency 0, IRQ 30 Memory at c0300000 (64-bit, non-prefetchable) [size=1M] Capabilities: [48] Vendor Specific Information: Len=08 <?> Capabilities: [50] Power Management version 3 Capabilities: [64] Express Endpoint, MSI 00 Capabilities: [a0] MSI: Enable- Count=1/8 Maskable- 64bit+ Capabilities: [c0] MSI-X: Enable+ Count=8 Masked- Kernel driver in use: xhci_hcd test progress: 1. cd /sys/kernel/debug/usb/xhci/0000:03:00.3/port_bandwidth# ls FS_BW HS_BW SS_BW 2. test fs speed device cat FS_BW port[1] available bw: 90%. port[2] available bw: 90%. port[3] available bw: 90%. port[4] available bw: 90%. port[5] available bw: 0%. port[6] available bw: 0%. port[7] available bw: 0%. port[8] available bw: 0%. plug in fs usb audio ID 0d8c:013c cat FS_BW port[1] available bw: 76%. port[2] available bw: 76%. port[3] available bw: 76%. port[4] available bw: 76%. port[5] available bw: 0%. port[6] available bw: 0%. port[7] available bw: 0%. port[8] available bw: 0%. 3. test hs speed device cat HS_BW port[1] available bw: 79%. port[2] available bw: 79%. port[3] available bw: 79%. port[4] available bw: 79%. port[5] available bw: 0%. port[6] available bw: 0%. port[7] available bw: 0%. port[8] available bw: 0%. plug in hs usb video ID 0408:1040 cat HS_BW port[1] available bw: 39%. port[2] available bw: 39%. port[3] available bw: 39%. port[4] available bw: 39%. port[5] available bw: 0%. port[6] available bw: 0%. port[7] available bw: 0%. port[8] available bw: 0%. 4.cat SS_BW port[1] available bw: 0%. port[2] available bw: 0%. port[3] available bw: 0%. port[4] available bw: 0%. port[5] available bw: 90%. port[6] available bw: 90%. port[7] available bw: 90%. port[8] available bw: 90%. Signed-off-by: Xu Rao <raoxu@uniontech.com> Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com> Link: https://lore.kernel.org/r/20250515135621.335595-3-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
597f5c2f41
commit
59d50e53e0
5 changed files with 215 additions and 1 deletions
|
|
@ -631,6 +631,112 @@ static void xhci_debugfs_create_ports(struct xhci_hcd *xhci,
|
|||
}
|
||||
}
|
||||
|
||||
static int xhci_port_bw_show(struct xhci_hcd *xhci, u8 dev_speed,
|
||||
struct seq_file *s)
|
||||
{
|
||||
unsigned int num_ports;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
struct xhci_container_ctx *ctx;
|
||||
struct usb_hcd *hcd = xhci_to_hcd(xhci);
|
||||
struct device *dev = hcd->self.controller;
|
||||
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
num_ports = HCS_MAX_PORTS(xhci->hcs_params1);
|
||||
|
||||
ctx = xhci_alloc_port_bw_ctx(xhci, 0);
|
||||
if (!ctx) {
|
||||
pm_runtime_put_sync(dev);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* get roothub port bandwidth */
|
||||
ret = xhci_get_port_bandwidth(xhci, ctx, dev_speed);
|
||||
if (ret)
|
||||
goto err_out;
|
||||
|
||||
/* print all roothub ports available bandwidth
|
||||
* refer to xhci rev1_2 protocol 6.2.6 , byte 0 is reserved
|
||||
*/
|
||||
for (i = 1; i < num_ports+1; i++)
|
||||
seq_printf(s, "port[%d] available bw: %d%%.\n", i,
|
||||
ctx->bytes[i]);
|
||||
err_out:
|
||||
pm_runtime_put_sync(dev);
|
||||
xhci_free_port_bw_ctx(xhci, ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int xhci_ss_bw_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
int ret;
|
||||
struct xhci_hcd *xhci = (struct xhci_hcd *)s->private;
|
||||
|
||||
ret = xhci_port_bw_show(xhci, USB_SPEED_SUPER, s);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int xhci_hs_bw_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
int ret;
|
||||
struct xhci_hcd *xhci = (struct xhci_hcd *)s->private;
|
||||
|
||||
ret = xhci_port_bw_show(xhci, USB_SPEED_HIGH, s);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int xhci_fs_bw_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
int ret;
|
||||
struct xhci_hcd *xhci = (struct xhci_hcd *)s->private;
|
||||
|
||||
ret = xhci_port_bw_show(xhci, USB_SPEED_FULL, s);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct xhci_file_map bw_context_files[] = {
|
||||
{"SS_BW", xhci_ss_bw_show, },
|
||||
{"HS_BW", xhci_hs_bw_show, },
|
||||
{"FS_BW", xhci_fs_bw_show, },
|
||||
};
|
||||
|
||||
static int bw_context_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
int i;
|
||||
struct xhci_file_map *f_map;
|
||||
const char *file_name = file_dentry(file)->d_iname;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(bw_context_files); i++) {
|
||||
f_map = &bw_context_files[i];
|
||||
|
||||
if (strcmp(f_map->name, file_name) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return single_open(file, f_map->show, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations bw_fops = {
|
||||
.open = bw_context_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static void xhci_debugfs_create_bandwidth(struct xhci_hcd *xhci,
|
||||
struct dentry *parent)
|
||||
{
|
||||
parent = debugfs_create_dir("port_bandwidth", parent);
|
||||
|
||||
xhci_debugfs_create_files(xhci, bw_context_files,
|
||||
ARRAY_SIZE(bw_context_files),
|
||||
xhci,
|
||||
parent, &bw_fops);
|
||||
}
|
||||
|
||||
void xhci_debugfs_init(struct xhci_hcd *xhci)
|
||||
{
|
||||
struct device *dev = xhci_to_hcd(xhci)->self.controller;
|
||||
|
|
@ -681,6 +787,8 @@ void xhci_debugfs_init(struct xhci_hcd *xhci)
|
|||
xhci->debugfs_slots = debugfs_create_dir("devices", xhci->debugfs_root);
|
||||
|
||||
xhci_debugfs_create_ports(xhci, xhci->debugfs_root);
|
||||
|
||||
xhci_debugfs_create_bandwidth(xhci, xhci->debugfs_root);
|
||||
}
|
||||
|
||||
void xhci_debugfs_exit(struct xhci_hcd *xhci)
|
||||
|
|
|
|||
|
|
@ -484,6 +484,35 @@ void xhci_free_container_ctx(struct xhci_hcd *xhci,
|
|||
kfree(ctx);
|
||||
}
|
||||
|
||||
struct xhci_container_ctx *xhci_alloc_port_bw_ctx(struct xhci_hcd *xhci,
|
||||
gfp_t flags)
|
||||
{
|
||||
struct xhci_container_ctx *ctx;
|
||||
struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
|
||||
|
||||
ctx = kzalloc_node(sizeof(*ctx), flags, dev_to_node(dev));
|
||||
if (!ctx)
|
||||
return NULL;
|
||||
|
||||
ctx->size = GET_PORT_BW_ARRAY_SIZE;
|
||||
|
||||
ctx->bytes = dma_pool_zalloc(xhci->port_bw_pool, flags, &ctx->dma);
|
||||
if (!ctx->bytes) {
|
||||
kfree(ctx);
|
||||
return NULL;
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void xhci_free_port_bw_ctx(struct xhci_hcd *xhci,
|
||||
struct xhci_container_ctx *ctx)
|
||||
{
|
||||
if (!ctx)
|
||||
return;
|
||||
dma_pool_free(xhci->port_bw_pool, ctx->bytes, ctx->dma);
|
||||
kfree(ctx);
|
||||
}
|
||||
|
||||
struct xhci_input_control_ctx *xhci_get_input_control_ctx(
|
||||
struct xhci_container_ctx *ctx)
|
||||
{
|
||||
|
|
@ -1912,6 +1941,11 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
|
|||
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
|
||||
"Freed small stream array pool");
|
||||
|
||||
dma_pool_destroy(xhci->port_bw_pool);
|
||||
xhci->port_bw_pool = NULL;
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
|
||||
"Freed xhci port bw array pool");
|
||||
|
||||
dma_pool_destroy(xhci->medium_streams_pool);
|
||||
xhci->medium_streams_pool = NULL;
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
|
||||
|
|
@ -2475,7 +2509,16 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
|
|||
* will be allocated with dma_alloc_coherent()
|
||||
*/
|
||||
|
||||
if (!xhci->small_streams_pool || !xhci->medium_streams_pool)
|
||||
/* refer to xhci rev1_2 protocol 5.3.3 max ports is 255.
|
||||
* refer to xhci rev1_2 protocol 6.4.3.14 port bandwidth buffer need
|
||||
* to be 16-byte aligned.
|
||||
*/
|
||||
xhci->port_bw_pool =
|
||||
dma_pool_create("xHCI 256 port bw ctx arrays",
|
||||
dev, GET_PORT_BW_ARRAY_SIZE, 16, 0);
|
||||
|
||||
if (!xhci->small_streams_pool || !xhci->medium_streams_pool ||
|
||||
!xhci->port_bw_pool)
|
||||
goto fail;
|
||||
|
||||
/* Set up the command ring to have one segments for now. */
|
||||
|
|
|
|||
|
|
@ -1898,6 +1898,8 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
|
|||
case TRB_NEC_GET_FW:
|
||||
xhci_handle_cmd_nec_get_fw(xhci, event);
|
||||
break;
|
||||
case TRB_GET_BW:
|
||||
break;
|
||||
default:
|
||||
/* Skip over unknown commands on the event ring */
|
||||
xhci_info(xhci, "INFO unknown command type %d\n", cmd_type);
|
||||
|
|
@ -4444,6 +4446,17 @@ int xhci_queue_configure_endpoint(struct xhci_hcd *xhci,
|
|||
command_must_succeed);
|
||||
}
|
||||
|
||||
/* Queue a get root hub port bandwidth command TRB */
|
||||
int xhci_queue_get_port_bw(struct xhci_hcd *xhci,
|
||||
struct xhci_command *cmd, dma_addr_t in_ctx_ptr,
|
||||
u8 dev_speed, bool command_must_succeed)
|
||||
{
|
||||
return queue_command(xhci, cmd, lower_32_bits(in_ctx_ptr),
|
||||
upper_32_bits(in_ctx_ptr), 0,
|
||||
TRB_TYPE(TRB_GET_BW) | DEV_SPEED_FOR_TRB(dev_speed),
|
||||
command_must_succeed);
|
||||
}
|
||||
|
||||
/* Queue an evaluate context command TRB */
|
||||
int xhci_queue_evaluate_context(struct xhci_hcd *xhci, struct xhci_command *cmd,
|
||||
dma_addr_t in_ctx_ptr, u32 slot_id, bool command_must_succeed)
|
||||
|
|
|
|||
|
|
@ -3090,6 +3090,42 @@ void xhci_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *udev)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(xhci_reset_bandwidth);
|
||||
|
||||
/* Get the available bandwidth of the ports under the xhci roothub */
|
||||
int xhci_get_port_bandwidth(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx,
|
||||
u8 dev_speed)
|
||||
{
|
||||
struct xhci_command *cmd;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
if (!ctx || !xhci)
|
||||
return -EINVAL;
|
||||
|
||||
cmd = xhci_alloc_command(xhci, true, GFP_KERNEL);
|
||||
if (!cmd)
|
||||
return -ENOMEM;
|
||||
|
||||
cmd->in_ctx = ctx;
|
||||
|
||||
/* get xhci port bandwidth, refer to xhci rev1_2 protocol 4.6.15 */
|
||||
spin_lock_irqsave(&xhci->lock, flags);
|
||||
|
||||
ret = xhci_queue_get_port_bw(xhci, cmd, ctx->dma, dev_speed, 0);
|
||||
if (ret) {
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
goto err_out;
|
||||
}
|
||||
xhci_ring_cmd_db(xhci);
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
|
||||
wait_for_completion(cmd->completion);
|
||||
err_out:
|
||||
kfree(cmd->completion);
|
||||
kfree(cmd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void xhci_setup_input_ctx_for_config_ep(struct xhci_hcd *xhci,
|
||||
struct xhci_container_ctx *in_ctx,
|
||||
struct xhci_container_ctx *out_ctx,
|
||||
|
|
|
|||
|
|
@ -589,6 +589,7 @@ struct xhci_stream_info {
|
|||
|
||||
#define SMALL_STREAM_ARRAY_SIZE 256
|
||||
#define MEDIUM_STREAM_ARRAY_SIZE 1024
|
||||
#define GET_PORT_BW_ARRAY_SIZE 256
|
||||
|
||||
/* Some Intel xHCI host controllers need software to keep track of the bus
|
||||
* bandwidth. Keep track of endpoint info here. Each root port is allocated
|
||||
|
|
@ -1006,6 +1007,9 @@ enum xhci_setup_dev {
|
|||
/* bits 16:23 are the virtual function ID */
|
||||
/* bits 24:31 are the slot ID */
|
||||
|
||||
/* bits 19:16 are the dev speed */
|
||||
#define DEV_SPEED_FOR_TRB(p) ((p) << 16)
|
||||
|
||||
/* Stop Endpoint TRB - ep_index to endpoint ID for this TRB */
|
||||
#define SUSPEND_PORT_FOR_TRB(p) (((p) & 1) << 23)
|
||||
#define TRB_TO_SUSPEND_PORT(p) (((p) & (1 << 23)) >> 23)
|
||||
|
|
@ -1558,6 +1562,7 @@ struct xhci_hcd {
|
|||
struct dma_pool *device_pool;
|
||||
struct dma_pool *segment_pool;
|
||||
struct dma_pool *small_streams_pool;
|
||||
struct dma_pool *port_bw_pool;
|
||||
struct dma_pool *medium_streams_pool;
|
||||
|
||||
/* Host controller watchdog timer structures */
|
||||
|
|
@ -1850,6 +1855,10 @@ struct xhci_container_ctx *xhci_alloc_container_ctx(struct xhci_hcd *xhci,
|
|||
int type, gfp_t flags);
|
||||
void xhci_free_container_ctx(struct xhci_hcd *xhci,
|
||||
struct xhci_container_ctx *ctx);
|
||||
struct xhci_container_ctx *xhci_alloc_port_bw_ctx(struct xhci_hcd *xhci,
|
||||
gfp_t flags);
|
||||
void xhci_free_port_bw_ctx(struct xhci_hcd *xhci,
|
||||
struct xhci_container_ctx *ctx);
|
||||
struct xhci_interrupter *
|
||||
xhci_create_secondary_interrupter(struct usb_hcd *hcd, unsigned int segs,
|
||||
u32 imod_interval, unsigned int intr_num);
|
||||
|
|
@ -1923,6 +1932,11 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags,
|
|||
int xhci_queue_configure_endpoint(struct xhci_hcd *xhci,
|
||||
struct xhci_command *cmd, dma_addr_t in_ctx_ptr, u32 slot_id,
|
||||
bool command_must_succeed);
|
||||
int xhci_queue_get_port_bw(struct xhci_hcd *xhci,
|
||||
struct xhci_command *cmd, dma_addr_t in_ctx_ptr,
|
||||
u8 dev_speed, bool command_must_succeed);
|
||||
int xhci_get_port_bandwidth(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx,
|
||||
u8 dev_speed);
|
||||
int xhci_queue_evaluate_context(struct xhci_hcd *xhci, struct xhci_command *cmd,
|
||||
dma_addr_t in_ctx_ptr, u32 slot_id, bool command_must_succeed);
|
||||
int xhci_queue_reset_ep(struct xhci_hcd *xhci, struct xhci_command *cmd,
|
||||
|
|
|
|||
Loading…
Reference in a new issue