1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Copyright (C) 2007 by Alan Stern 4 */ 5 6 /* this file is part of ehci-hcd.c */ 7 8 9 /* Display the ports dedicated to the companion controller */ 10 static ssize_t show_companion(struct device *dev, 11 struct device_attribute *attr, 12 char *buf) 13 { 14 struct ehci_hcd *ehci; 15 int nports, index, n; 16 int count = PAGE_SIZE; 17 char *ptr = buf; 18 19 ehci = hcd_to_ehci(dev_get_drvdata(dev)); 20 nports = HCS_N_PORTS(ehci->hcs_params); 21 22 for (index = 0; index < nports; ++index) { 23 if (test_bit(index, &ehci->companion_ports)) { 24 n = scnprintf(ptr, count, "%d\n", index + 1); 25 ptr += n; 26 count -= n; 27 } 28 } 29 return ptr - buf; 30 } 31 32 /* 33 * Dedicate or undedicate a port to the companion controller. 34 * Syntax is "[-]portnum", where a leading '-' sign means 35 * return control of the port to the EHCI controller. 36 */ 37 static ssize_t store_companion(struct device *dev, 38 struct device_attribute *attr, 39 const char *buf, size_t count) 40 { 41 struct ehci_hcd *ehci; 42 int portnum, new_owner; 43 44 ehci = hcd_to_ehci(dev_get_drvdata(dev)); 45 new_owner = PORT_OWNER; /* Owned by companion */ 46 if (sscanf(buf, "%d", &portnum) != 1) 47 return -EINVAL; 48 if (portnum < 0) { 49 portnum = - portnum; 50 new_owner = 0; /* Owned by EHCI */ 51 } 52 if (portnum <= 0 || portnum > HCS_N_PORTS(ehci->hcs_params)) 53 return -ENOENT; 54 portnum--; 55 if (new_owner) 56 set_bit(portnum, &ehci->companion_ports); 57 else 58 clear_bit(portnum, &ehci->companion_ports); 59 set_owner(ehci, portnum, new_owner); 60 return count; 61 } 62 static DEVICE_ATTR(companion, 0644, show_companion, store_companion); 63 64 65 /* 66 * Display / Set uframe_periodic_max 67 */ 68 static ssize_t show_uframe_periodic_max(struct device *dev, 69 struct device_attribute *attr, 70 char *buf) 71 { 72 struct ehci_hcd *ehci; 73 int n; 74 75 ehci = hcd_to_ehci(dev_get_drvdata(dev)); 76 n = scnprintf(buf, PAGE_SIZE, "%d\n", ehci->uframe_periodic_max); 77 return n; 78 } 79 80 81 static ssize_t store_uframe_periodic_max(struct device *dev, 82 struct device_attribute *attr, 83 const char *buf, size_t count) 84 { 85 struct ehci_hcd *ehci; 86 unsigned uframe_periodic_max; 87 unsigned uframe; 88 unsigned long flags; 89 ssize_t ret; 90 91 ehci = hcd_to_ehci(dev_get_drvdata(dev)); 92 if (kstrtouint(buf, 0, &uframe_periodic_max) < 0) 93 return -EINVAL; 94 95 if (uframe_periodic_max < 100 || uframe_periodic_max >= 125) { 96 ehci_info(ehci, "rejecting invalid request for " 97 "uframe_periodic_max=%u\n", uframe_periodic_max); 98 return -EINVAL; 99 } 100 101 ret = -EINVAL; 102 103 /* 104 * lock, so that our checking does not race with possible periodic 105 * bandwidth allocation through submitting new urbs. 106 */ 107 spin_lock_irqsave (&ehci->lock, flags); 108 109 /* 110 * for request to decrease max periodic bandwidth, we have to check 111 * to see whether the decrease is possible. 112 */ 113 if (uframe_periodic_max < ehci->uframe_periodic_max) { 114 u8 allocated_max = 0; 115 116 for (uframe = 0; uframe < EHCI_BANDWIDTH_SIZE; ++uframe) 117 allocated_max = max(allocated_max, 118 ehci->bandwidth[uframe]); 119 120 if (allocated_max > uframe_periodic_max) { 121 ehci_info(ehci, 122 "cannot decrease uframe_periodic_max because " 123 "periodic bandwidth is already allocated " 124 "(%u > %u)\n", 125 allocated_max, uframe_periodic_max); 126 goto out_unlock; 127 } 128 } 129 130 /* increasing is always ok */ 131 132 ehci_info(ehci, "setting max periodic bandwidth to %u%% " 133 "(== %u usec/uframe)\n", 134 100*uframe_periodic_max/125, uframe_periodic_max); 135 136 if (uframe_periodic_max != 100) 137 ehci_warn(ehci, "max periodic bandwidth set is non-standard\n"); 138 139 ehci->uframe_periodic_max = uframe_periodic_max; 140 ret = count; 141 142 out_unlock: 143 spin_unlock_irqrestore (&ehci->lock, flags); 144 return ret; 145 } 146 static DEVICE_ATTR(uframe_periodic_max, 0644, show_uframe_periodic_max, store_uframe_periodic_max); 147 148 149 static inline int create_sysfs_files(struct ehci_hcd *ehci) 150 { 151 struct device *controller = ehci_to_hcd(ehci)->self.controller; 152 int i = 0; 153 154 /* with integrated TT there is no companion! */ 155 if (!ehci_is_TDI(ehci)) 156 i = device_create_file(controller, &dev_attr_companion); 157 if (i) 158 goto out; 159 160 i = device_create_file(controller, &dev_attr_uframe_periodic_max); 161 out: 162 return i; 163 } 164 165 static inline void remove_sysfs_files(struct ehci_hcd *ehci) 166 { 167 struct device *controller = ehci_to_hcd(ehci)->self.controller; 168 169 /* with integrated TT there is no companion! */ 170 if (!ehci_is_TDI(ehci)) 171 device_remove_file(controller, &dev_attr_companion); 172 173 device_remove_file(controller, &dev_attr_uframe_periodic_max); 174 } 175