GNU Linux-libre 4.19.286-gnu1
[releases.git] / drivers / usb / host / ehci-sysfs.c
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(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 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(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_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(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 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(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_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(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 }