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, n; |
16 | int count = PAGE_SIZE; |
17 | char *ptr = buf; |
18 | |
19 | ehci = hcd_to_ehci(hcd: 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(buf: ptr, size: count, fmt: "%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 companion_store(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(hcd: 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(nr: portnum, addr: &ehci->companion_ports); |
57 | else |
58 | clear_bit(nr: portnum, addr: &ehci->companion_ports); |
59 | set_owner(ehci, portnum, new_owner); |
60 | return count; |
61 | } |
62 | static DEVICE_ATTR_RW(companion); |
63 | |
64 | |
65 | /* |
66 | * Display / Set uframe_periodic_max |
67 | */ |
68 | static ssize_t uframe_periodic_max_show(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(hcd: dev_get_drvdata(dev)); |
76 | n = scnprintf(buf, PAGE_SIZE, fmt: "%d\n" , ehci->uframe_periodic_max); |
77 | return n; |
78 | } |
79 | |
80 | |
81 | static ssize_t uframe_periodic_max_store(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(hcd: dev_get_drvdata(dev)); |
92 | if (kstrtouint(s: buf, base: 0, res: &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 (lock: &ehci->lock, flags); |
144 | return ret; |
145 | } |
146 | static DEVICE_ATTR_RW(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(device: controller, entry: &dev_attr_companion); |
157 | if (i) |
158 | goto out; |
159 | |
160 | i = device_create_file(device: controller, entry: &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(dev: controller, attr: &dev_attr_companion); |
172 | |
173 | device_remove_file(dev: controller, attr: &dev_attr_uframe_periodic_max); |
174 | } |
175 | |