xref: /linux/drivers/usb/host/ehci-sysfs.c (revision 552c69b36ebd966186573b9c7a286b390935cce1)
15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+
24c67045bSKirill Smelkov /*
34c67045bSKirill Smelkov  * Copyright (C) 2007 by Alan Stern
44c67045bSKirill Smelkov  */
54c67045bSKirill Smelkov 
64c67045bSKirill Smelkov /* this file is part of ehci-hcd.c */
74c67045bSKirill Smelkov 
84c67045bSKirill Smelkov 
94c67045bSKirill Smelkov /* Display the ports dedicated to the companion controller */
companion_show(struct device * dev,struct device_attribute * attr,char * buf)10*ed5bd7a4SGreg Kroah-Hartman static ssize_t companion_show(struct device *dev,
114c67045bSKirill Smelkov 			      struct device_attribute *attr,
124c67045bSKirill Smelkov 			      char *buf)
134c67045bSKirill Smelkov {
144c67045bSKirill Smelkov 	struct ehci_hcd		*ehci;
154c67045bSKirill Smelkov 	int			nports, index, n;
164c67045bSKirill Smelkov 	int			count = PAGE_SIZE;
174c67045bSKirill Smelkov 	char			*ptr = buf;
184c67045bSKirill Smelkov 
190521cfd0SPeter Chen 	ehci = hcd_to_ehci(dev_get_drvdata(dev));
204c67045bSKirill Smelkov 	nports = HCS_N_PORTS(ehci->hcs_params);
214c67045bSKirill Smelkov 
224c67045bSKirill Smelkov 	for (index = 0; index < nports; ++index) {
234c67045bSKirill Smelkov 		if (test_bit(index, &ehci->companion_ports)) {
244c67045bSKirill Smelkov 			n = scnprintf(ptr, count, "%d\n", index + 1);
254c67045bSKirill Smelkov 			ptr += n;
264c67045bSKirill Smelkov 			count -= n;
274c67045bSKirill Smelkov 		}
284c67045bSKirill Smelkov 	}
294c67045bSKirill Smelkov 	return ptr - buf;
304c67045bSKirill Smelkov }
314c67045bSKirill Smelkov 
324c67045bSKirill Smelkov /*
334c67045bSKirill Smelkov  * Dedicate or undedicate a port to the companion controller.
344c67045bSKirill Smelkov  * Syntax is "[-]portnum", where a leading '-' sign means
354c67045bSKirill Smelkov  * return control of the port to the EHCI controller.
364c67045bSKirill Smelkov  */
companion_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)37*ed5bd7a4SGreg Kroah-Hartman static ssize_t companion_store(struct device *dev,
384c67045bSKirill Smelkov 			       struct device_attribute *attr,
394c67045bSKirill Smelkov 			       const char *buf, size_t count)
404c67045bSKirill Smelkov {
414c67045bSKirill Smelkov 	struct ehci_hcd		*ehci;
424c67045bSKirill Smelkov 	int			portnum, new_owner;
434c67045bSKirill Smelkov 
440521cfd0SPeter Chen 	ehci = hcd_to_ehci(dev_get_drvdata(dev));
454c67045bSKirill Smelkov 	new_owner = PORT_OWNER;		/* Owned by companion */
464c67045bSKirill Smelkov 	if (sscanf(buf, "%d", &portnum) != 1)
474c67045bSKirill Smelkov 		return -EINVAL;
484c67045bSKirill Smelkov 	if (portnum < 0) {
494c67045bSKirill Smelkov 		portnum = - portnum;
504c67045bSKirill Smelkov 		new_owner = 0;		/* Owned by EHCI */
514c67045bSKirill Smelkov 	}
524c67045bSKirill Smelkov 	if (portnum <= 0 || portnum > HCS_N_PORTS(ehci->hcs_params))
534c67045bSKirill Smelkov 		return -ENOENT;
544c67045bSKirill Smelkov 	portnum--;
554c67045bSKirill Smelkov 	if (new_owner)
564c67045bSKirill Smelkov 		set_bit(portnum, &ehci->companion_ports);
574c67045bSKirill Smelkov 	else
584c67045bSKirill Smelkov 		clear_bit(portnum, &ehci->companion_ports);
594c67045bSKirill Smelkov 	set_owner(ehci, portnum, new_owner);
604c67045bSKirill Smelkov 	return count;
614c67045bSKirill Smelkov }
62*ed5bd7a4SGreg Kroah-Hartman static DEVICE_ATTR_RW(companion);
634c67045bSKirill Smelkov 
64cc62a7ebSKirill Smelkov 
65cc62a7ebSKirill Smelkov /*
66cc62a7ebSKirill Smelkov  * Display / Set uframe_periodic_max
67cc62a7ebSKirill Smelkov  */
uframe_periodic_max_show(struct device * dev,struct device_attribute * attr,char * buf)68*ed5bd7a4SGreg Kroah-Hartman static ssize_t uframe_periodic_max_show(struct device *dev,
69cc62a7ebSKirill Smelkov 					struct device_attribute *attr,
70cc62a7ebSKirill Smelkov 					char *buf)
71cc62a7ebSKirill Smelkov {
72cc62a7ebSKirill Smelkov 	struct ehci_hcd		*ehci;
73cc62a7ebSKirill Smelkov 	int			n;
74cc62a7ebSKirill Smelkov 
750521cfd0SPeter Chen 	ehci = hcd_to_ehci(dev_get_drvdata(dev));
76cc62a7ebSKirill Smelkov 	n = scnprintf(buf, PAGE_SIZE, "%d\n", ehci->uframe_periodic_max);
77cc62a7ebSKirill Smelkov 	return n;
78cc62a7ebSKirill Smelkov }
79cc62a7ebSKirill Smelkov 
80cc62a7ebSKirill Smelkov 
uframe_periodic_max_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)81*ed5bd7a4SGreg Kroah-Hartman static ssize_t uframe_periodic_max_store(struct device *dev,
82cc62a7ebSKirill Smelkov 					struct device_attribute *attr,
83cc62a7ebSKirill Smelkov 					const char *buf, size_t count)
84cc62a7ebSKirill Smelkov {
85cc62a7ebSKirill Smelkov 	struct ehci_hcd		*ehci;
86cc62a7ebSKirill Smelkov 	unsigned		uframe_periodic_max;
87d0ce5c6bSAlan Stern 	unsigned		uframe;
88cc62a7ebSKirill Smelkov 	unsigned long		flags;
89cc62a7ebSKirill Smelkov 	ssize_t			ret;
90cc62a7ebSKirill Smelkov 
910521cfd0SPeter Chen 	ehci = hcd_to_ehci(dev_get_drvdata(dev));
92cc62a7ebSKirill Smelkov 	if (kstrtouint(buf, 0, &uframe_periodic_max) < 0)
93cc62a7ebSKirill Smelkov 		return -EINVAL;
94cc62a7ebSKirill Smelkov 
95cc62a7ebSKirill Smelkov 	if (uframe_periodic_max < 100 || uframe_periodic_max >= 125) {
96cc62a7ebSKirill Smelkov 		ehci_info(ehci, "rejecting invalid request for "
97cc62a7ebSKirill Smelkov 				"uframe_periodic_max=%u\n", uframe_periodic_max);
98cc62a7ebSKirill Smelkov 		return -EINVAL;
99cc62a7ebSKirill Smelkov 	}
100cc62a7ebSKirill Smelkov 
101cc62a7ebSKirill Smelkov 	ret = -EINVAL;
102cc62a7ebSKirill Smelkov 
103cc62a7ebSKirill Smelkov 	/*
104cc62a7ebSKirill Smelkov 	 * lock, so that our checking does not race with possible periodic
105cc62a7ebSKirill Smelkov 	 * bandwidth allocation through submitting new urbs.
106cc62a7ebSKirill Smelkov 	 */
107cc62a7ebSKirill Smelkov 	spin_lock_irqsave (&ehci->lock, flags);
108cc62a7ebSKirill Smelkov 
109cc62a7ebSKirill Smelkov 	/*
110cc62a7ebSKirill Smelkov 	 * for request to decrease max periodic bandwidth, we have to check
111d0ce5c6bSAlan Stern 	 * to see whether the decrease is possible.
112cc62a7ebSKirill Smelkov 	 */
113cc62a7ebSKirill Smelkov 	if (uframe_periodic_max < ehci->uframe_periodic_max) {
114d0ce5c6bSAlan Stern 		u8		allocated_max = 0;
115cc62a7ebSKirill Smelkov 
116d0ce5c6bSAlan Stern 		for (uframe = 0; uframe < EHCI_BANDWIDTH_SIZE; ++uframe)
117cc62a7ebSKirill Smelkov 			allocated_max = max(allocated_max,
118d0ce5c6bSAlan Stern 					ehci->bandwidth[uframe]);
119cc62a7ebSKirill Smelkov 
120cc62a7ebSKirill Smelkov 		if (allocated_max > uframe_periodic_max) {
121cc62a7ebSKirill Smelkov 			ehci_info(ehci,
1226774def6SMasanari Iida 				"cannot decrease uframe_periodic_max because "
123cc62a7ebSKirill Smelkov 				"periodic bandwidth is already allocated "
124cc62a7ebSKirill Smelkov 				"(%u > %u)\n",
125cc62a7ebSKirill Smelkov 				allocated_max, uframe_periodic_max);
126cc62a7ebSKirill Smelkov 			goto out_unlock;
127cc62a7ebSKirill Smelkov 		}
128cc62a7ebSKirill Smelkov 	}
129cc62a7ebSKirill Smelkov 
130cc62a7ebSKirill Smelkov 	/* increasing is always ok */
131cc62a7ebSKirill Smelkov 
132cc62a7ebSKirill Smelkov 	ehci_info(ehci, "setting max periodic bandwidth to %u%% "
133cc62a7ebSKirill Smelkov 			"(== %u usec/uframe)\n",
134cc62a7ebSKirill Smelkov 			100*uframe_periodic_max/125, uframe_periodic_max);
135cc62a7ebSKirill Smelkov 
136cc62a7ebSKirill Smelkov 	if (uframe_periodic_max != 100)
137cc62a7ebSKirill Smelkov 		ehci_warn(ehci, "max periodic bandwidth set is non-standard\n");
138cc62a7ebSKirill Smelkov 
139cc62a7ebSKirill Smelkov 	ehci->uframe_periodic_max = uframe_periodic_max;
140cc62a7ebSKirill Smelkov 	ret = count;
141cc62a7ebSKirill Smelkov 
142cc62a7ebSKirill Smelkov out_unlock:
143cc62a7ebSKirill Smelkov 	spin_unlock_irqrestore (&ehci->lock, flags);
144cc62a7ebSKirill Smelkov 	return ret;
145cc62a7ebSKirill Smelkov }
146*ed5bd7a4SGreg Kroah-Hartman static DEVICE_ATTR_RW(uframe_periodic_max);
147cc62a7ebSKirill Smelkov 
148cc62a7ebSKirill Smelkov 
create_sysfs_files(struct ehci_hcd * ehci)1494c67045bSKirill Smelkov static inline int create_sysfs_files(struct ehci_hcd *ehci)
1504c67045bSKirill Smelkov {
151cc62a7ebSKirill Smelkov 	struct device	*controller = ehci_to_hcd(ehci)->self.controller;
1524c67045bSKirill Smelkov 	int	i = 0;
1534c67045bSKirill Smelkov 
1544c67045bSKirill Smelkov 	/* with integrated TT there is no companion! */
1554c67045bSKirill Smelkov 	if (!ehci_is_TDI(ehci))
156cc62a7ebSKirill Smelkov 		i = device_create_file(controller, &dev_attr_companion);
157cc62a7ebSKirill Smelkov 	if (i)
158cc62a7ebSKirill Smelkov 		goto out;
159cc62a7ebSKirill Smelkov 
160cc62a7ebSKirill Smelkov 	i = device_create_file(controller, &dev_attr_uframe_periodic_max);
161cc62a7ebSKirill Smelkov out:
1624c67045bSKirill Smelkov 	return i;
1634c67045bSKirill Smelkov }
1644c67045bSKirill Smelkov 
remove_sysfs_files(struct ehci_hcd * ehci)1654c67045bSKirill Smelkov static inline void remove_sysfs_files(struct ehci_hcd *ehci)
1664c67045bSKirill Smelkov {
167cc62a7ebSKirill Smelkov 	struct device	*controller = ehci_to_hcd(ehci)->self.controller;
168cc62a7ebSKirill Smelkov 
1694c67045bSKirill Smelkov 	/* with integrated TT there is no companion! */
1704c67045bSKirill Smelkov 	if (!ehci_is_TDI(ehci))
171cc62a7ebSKirill Smelkov 		device_remove_file(controller, &dev_attr_companion);
172cc62a7ebSKirill Smelkov 
173cc62a7ebSKirill Smelkov 	device_remove_file(controller, &dev_attr_uframe_periodic_max);
1744c67045bSKirill Smelkov }
175