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