xref: /linux/drivers/usb/host/ehci-sysfs.c (revision 5fd54ace4721fc5ce2bb5aef6318fcf17f421460)
1*5fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+
24c67045bSKirill Smelkov /*
34c67045bSKirill Smelkov  * Copyright (C) 2007 by Alan Stern
44c67045bSKirill Smelkov  *
54c67045bSKirill Smelkov  * This program is free software; you can redistribute it and/or modify it
64c67045bSKirill Smelkov  * under the terms of the GNU General Public License as published by the
74c67045bSKirill Smelkov  * Free Software Foundation; either version 2 of the License, or (at your
84c67045bSKirill Smelkov  * option) any later version.
94c67045bSKirill Smelkov  *
104c67045bSKirill Smelkov  * This program is distributed in the hope that it will be useful, but
114c67045bSKirill Smelkov  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
124c67045bSKirill Smelkov  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
134c67045bSKirill Smelkov  * for more details.
144c67045bSKirill Smelkov  *
154c67045bSKirill Smelkov  * You should have received a copy of the GNU General Public License
164c67045bSKirill Smelkov  * along with this program; if not, write to the Free Software Foundation,
174c67045bSKirill Smelkov  * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
184c67045bSKirill Smelkov  */
194c67045bSKirill Smelkov 
204c67045bSKirill Smelkov /* this file is part of ehci-hcd.c */
214c67045bSKirill Smelkov 
224c67045bSKirill Smelkov 
234c67045bSKirill Smelkov /* Display the ports dedicated to the companion controller */
244c67045bSKirill Smelkov static ssize_t show_companion(struct device *dev,
254c67045bSKirill Smelkov 			      struct device_attribute *attr,
264c67045bSKirill Smelkov 			      char *buf)
274c67045bSKirill Smelkov {
284c67045bSKirill Smelkov 	struct ehci_hcd		*ehci;
294c67045bSKirill Smelkov 	int			nports, index, n;
304c67045bSKirill Smelkov 	int			count = PAGE_SIZE;
314c67045bSKirill Smelkov 	char			*ptr = buf;
324c67045bSKirill Smelkov 
330521cfd0SPeter Chen 	ehci = hcd_to_ehci(dev_get_drvdata(dev));
344c67045bSKirill Smelkov 	nports = HCS_N_PORTS(ehci->hcs_params);
354c67045bSKirill Smelkov 
364c67045bSKirill Smelkov 	for (index = 0; index < nports; ++index) {
374c67045bSKirill Smelkov 		if (test_bit(index, &ehci->companion_ports)) {
384c67045bSKirill Smelkov 			n = scnprintf(ptr, count, "%d\n", index + 1);
394c67045bSKirill Smelkov 			ptr += n;
404c67045bSKirill Smelkov 			count -= n;
414c67045bSKirill Smelkov 		}
424c67045bSKirill Smelkov 	}
434c67045bSKirill Smelkov 	return ptr - buf;
444c67045bSKirill Smelkov }
454c67045bSKirill Smelkov 
464c67045bSKirill Smelkov /*
474c67045bSKirill Smelkov  * Dedicate or undedicate a port to the companion controller.
484c67045bSKirill Smelkov  * Syntax is "[-]portnum", where a leading '-' sign means
494c67045bSKirill Smelkov  * return control of the port to the EHCI controller.
504c67045bSKirill Smelkov  */
514c67045bSKirill Smelkov static ssize_t store_companion(struct device *dev,
524c67045bSKirill Smelkov 			       struct device_attribute *attr,
534c67045bSKirill Smelkov 			       const char *buf, size_t count)
544c67045bSKirill Smelkov {
554c67045bSKirill Smelkov 	struct ehci_hcd		*ehci;
564c67045bSKirill Smelkov 	int			portnum, new_owner;
574c67045bSKirill Smelkov 
580521cfd0SPeter Chen 	ehci = hcd_to_ehci(dev_get_drvdata(dev));
594c67045bSKirill Smelkov 	new_owner = PORT_OWNER;		/* Owned by companion */
604c67045bSKirill Smelkov 	if (sscanf(buf, "%d", &portnum) != 1)
614c67045bSKirill Smelkov 		return -EINVAL;
624c67045bSKirill Smelkov 	if (portnum < 0) {
634c67045bSKirill Smelkov 		portnum = - portnum;
644c67045bSKirill Smelkov 		new_owner = 0;		/* Owned by EHCI */
654c67045bSKirill Smelkov 	}
664c67045bSKirill Smelkov 	if (portnum <= 0 || portnum > HCS_N_PORTS(ehci->hcs_params))
674c67045bSKirill Smelkov 		return -ENOENT;
684c67045bSKirill Smelkov 	portnum--;
694c67045bSKirill Smelkov 	if (new_owner)
704c67045bSKirill Smelkov 		set_bit(portnum, &ehci->companion_ports);
714c67045bSKirill Smelkov 	else
724c67045bSKirill Smelkov 		clear_bit(portnum, &ehci->companion_ports);
734c67045bSKirill Smelkov 	set_owner(ehci, portnum, new_owner);
744c67045bSKirill Smelkov 	return count;
754c67045bSKirill Smelkov }
764c67045bSKirill Smelkov static DEVICE_ATTR(companion, 0644, show_companion, store_companion);
774c67045bSKirill Smelkov 
78cc62a7ebSKirill Smelkov 
79cc62a7ebSKirill Smelkov /*
80cc62a7ebSKirill Smelkov  * Display / Set uframe_periodic_max
81cc62a7ebSKirill Smelkov  */
82cc62a7ebSKirill Smelkov static ssize_t show_uframe_periodic_max(struct device *dev,
83cc62a7ebSKirill Smelkov 					struct device_attribute *attr,
84cc62a7ebSKirill Smelkov 					char *buf)
85cc62a7ebSKirill Smelkov {
86cc62a7ebSKirill Smelkov 	struct ehci_hcd		*ehci;
87cc62a7ebSKirill Smelkov 	int			n;
88cc62a7ebSKirill Smelkov 
890521cfd0SPeter Chen 	ehci = hcd_to_ehci(dev_get_drvdata(dev));
90cc62a7ebSKirill Smelkov 	n = scnprintf(buf, PAGE_SIZE, "%d\n", ehci->uframe_periodic_max);
91cc62a7ebSKirill Smelkov 	return n;
92cc62a7ebSKirill Smelkov }
93cc62a7ebSKirill Smelkov 
94cc62a7ebSKirill Smelkov 
95cc62a7ebSKirill Smelkov static ssize_t store_uframe_periodic_max(struct device *dev,
96cc62a7ebSKirill Smelkov 					struct device_attribute *attr,
97cc62a7ebSKirill Smelkov 					const char *buf, size_t count)
98cc62a7ebSKirill Smelkov {
99cc62a7ebSKirill Smelkov 	struct ehci_hcd		*ehci;
100cc62a7ebSKirill Smelkov 	unsigned		uframe_periodic_max;
101d0ce5c6bSAlan Stern 	unsigned		uframe;
102cc62a7ebSKirill Smelkov 	unsigned long		flags;
103cc62a7ebSKirill Smelkov 	ssize_t			ret;
104cc62a7ebSKirill Smelkov 
1050521cfd0SPeter Chen 	ehci = hcd_to_ehci(dev_get_drvdata(dev));
106cc62a7ebSKirill Smelkov 	if (kstrtouint(buf, 0, &uframe_periodic_max) < 0)
107cc62a7ebSKirill Smelkov 		return -EINVAL;
108cc62a7ebSKirill Smelkov 
109cc62a7ebSKirill Smelkov 	if (uframe_periodic_max < 100 || uframe_periodic_max >= 125) {
110cc62a7ebSKirill Smelkov 		ehci_info(ehci, "rejecting invalid request for "
111cc62a7ebSKirill Smelkov 				"uframe_periodic_max=%u\n", uframe_periodic_max);
112cc62a7ebSKirill Smelkov 		return -EINVAL;
113cc62a7ebSKirill Smelkov 	}
114cc62a7ebSKirill Smelkov 
115cc62a7ebSKirill Smelkov 	ret = -EINVAL;
116cc62a7ebSKirill Smelkov 
117cc62a7ebSKirill Smelkov 	/*
118cc62a7ebSKirill Smelkov 	 * lock, so that our checking does not race with possible periodic
119cc62a7ebSKirill Smelkov 	 * bandwidth allocation through submitting new urbs.
120cc62a7ebSKirill Smelkov 	 */
121cc62a7ebSKirill Smelkov 	spin_lock_irqsave (&ehci->lock, flags);
122cc62a7ebSKirill Smelkov 
123cc62a7ebSKirill Smelkov 	/*
124cc62a7ebSKirill Smelkov 	 * for request to decrease max periodic bandwidth, we have to check
125d0ce5c6bSAlan Stern 	 * to see whether the decrease is possible.
126cc62a7ebSKirill Smelkov 	 */
127cc62a7ebSKirill Smelkov 	if (uframe_periodic_max < ehci->uframe_periodic_max) {
128d0ce5c6bSAlan Stern 		u8		allocated_max = 0;
129cc62a7ebSKirill Smelkov 
130d0ce5c6bSAlan Stern 		for (uframe = 0; uframe < EHCI_BANDWIDTH_SIZE; ++uframe)
131cc62a7ebSKirill Smelkov 			allocated_max = max(allocated_max,
132d0ce5c6bSAlan Stern 					ehci->bandwidth[uframe]);
133cc62a7ebSKirill Smelkov 
134cc62a7ebSKirill Smelkov 		if (allocated_max > uframe_periodic_max) {
135cc62a7ebSKirill Smelkov 			ehci_info(ehci,
1366774def6SMasanari Iida 				"cannot decrease uframe_periodic_max because "
137cc62a7ebSKirill Smelkov 				"periodic bandwidth is already allocated "
138cc62a7ebSKirill Smelkov 				"(%u > %u)\n",
139cc62a7ebSKirill Smelkov 				allocated_max, uframe_periodic_max);
140cc62a7ebSKirill Smelkov 			goto out_unlock;
141cc62a7ebSKirill Smelkov 		}
142cc62a7ebSKirill Smelkov 	}
143cc62a7ebSKirill Smelkov 
144cc62a7ebSKirill Smelkov 	/* increasing is always ok */
145cc62a7ebSKirill Smelkov 
146cc62a7ebSKirill Smelkov 	ehci_info(ehci, "setting max periodic bandwidth to %u%% "
147cc62a7ebSKirill Smelkov 			"(== %u usec/uframe)\n",
148cc62a7ebSKirill Smelkov 			100*uframe_periodic_max/125, uframe_periodic_max);
149cc62a7ebSKirill Smelkov 
150cc62a7ebSKirill Smelkov 	if (uframe_periodic_max != 100)
151cc62a7ebSKirill Smelkov 		ehci_warn(ehci, "max periodic bandwidth set is non-standard\n");
152cc62a7ebSKirill Smelkov 
153cc62a7ebSKirill Smelkov 	ehci->uframe_periodic_max = uframe_periodic_max;
154cc62a7ebSKirill Smelkov 	ret = count;
155cc62a7ebSKirill Smelkov 
156cc62a7ebSKirill Smelkov out_unlock:
157cc62a7ebSKirill Smelkov 	spin_unlock_irqrestore (&ehci->lock, flags);
158cc62a7ebSKirill Smelkov 	return ret;
159cc62a7ebSKirill Smelkov }
160cc62a7ebSKirill Smelkov static DEVICE_ATTR(uframe_periodic_max, 0644, show_uframe_periodic_max, store_uframe_periodic_max);
161cc62a7ebSKirill Smelkov 
162cc62a7ebSKirill Smelkov 
1634c67045bSKirill Smelkov static inline int create_sysfs_files(struct ehci_hcd *ehci)
1644c67045bSKirill Smelkov {
165cc62a7ebSKirill Smelkov 	struct device	*controller = ehci_to_hcd(ehci)->self.controller;
1664c67045bSKirill Smelkov 	int	i = 0;
1674c67045bSKirill Smelkov 
1684c67045bSKirill Smelkov 	/* with integrated TT there is no companion! */
1694c67045bSKirill Smelkov 	if (!ehci_is_TDI(ehci))
170cc62a7ebSKirill Smelkov 		i = device_create_file(controller, &dev_attr_companion);
171cc62a7ebSKirill Smelkov 	if (i)
172cc62a7ebSKirill Smelkov 		goto out;
173cc62a7ebSKirill Smelkov 
174cc62a7ebSKirill Smelkov 	i = device_create_file(controller, &dev_attr_uframe_periodic_max);
175cc62a7ebSKirill Smelkov out:
1764c67045bSKirill Smelkov 	return i;
1774c67045bSKirill Smelkov }
1784c67045bSKirill Smelkov 
1794c67045bSKirill Smelkov static inline void remove_sysfs_files(struct ehci_hcd *ehci)
1804c67045bSKirill Smelkov {
181cc62a7ebSKirill Smelkov 	struct device	*controller = ehci_to_hcd(ehci)->self.controller;
182cc62a7ebSKirill Smelkov 
1834c67045bSKirill Smelkov 	/* with integrated TT there is no companion! */
1844c67045bSKirill Smelkov 	if (!ehci_is_TDI(ehci))
185cc62a7ebSKirill Smelkov 		device_remove_file(controller, &dev_attr_companion);
186cc62a7ebSKirill Smelkov 
187cc62a7ebSKirill Smelkov 	device_remove_file(controller, &dev_attr_uframe_periodic_max);
1884c67045bSKirill Smelkov }
189