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