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