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
	
	 Xu Rao
						Xu Rao