15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+
24c67045bSKirill Smelkov /*
34c67045bSKirill Smelkov * Copyright (C) 2007 by Alan Stern
44c67045bSKirill Smelkov */
54c67045bSKirill Smelkov
64c67045bSKirill Smelkov /* this file is part of ehci-hcd.c */
74c67045bSKirill Smelkov
84c67045bSKirill Smelkov
94c67045bSKirill Smelkov /* Display the ports dedicated to the companion controller */
companion_show(struct device * dev,struct device_attribute * attr,char * buf)10*ed5bd7a4SGreg Kroah-Hartman static ssize_t companion_show(struct device *dev,
114c67045bSKirill Smelkov struct device_attribute *attr,
124c67045bSKirill Smelkov char *buf)
134c67045bSKirill Smelkov {
144c67045bSKirill Smelkov struct ehci_hcd *ehci;
154c67045bSKirill Smelkov int nports, index, n;
164c67045bSKirill Smelkov int count = PAGE_SIZE;
174c67045bSKirill Smelkov char *ptr = buf;
184c67045bSKirill Smelkov
190521cfd0SPeter Chen ehci = hcd_to_ehci(dev_get_drvdata(dev));
204c67045bSKirill Smelkov nports = HCS_N_PORTS(ehci->hcs_params);
214c67045bSKirill Smelkov
224c67045bSKirill Smelkov for (index = 0; index < nports; ++index) {
234c67045bSKirill Smelkov if (test_bit(index, &ehci->companion_ports)) {
244c67045bSKirill Smelkov n = scnprintf(ptr, count, "%d\n", index + 1);
254c67045bSKirill Smelkov ptr += n;
264c67045bSKirill Smelkov count -= n;
274c67045bSKirill Smelkov }
284c67045bSKirill Smelkov }
294c67045bSKirill Smelkov return ptr - buf;
304c67045bSKirill Smelkov }
314c67045bSKirill Smelkov
324c67045bSKirill Smelkov /*
334c67045bSKirill Smelkov * Dedicate or undedicate a port to the companion controller.
344c67045bSKirill Smelkov * Syntax is "[-]portnum", where a leading '-' sign means
354c67045bSKirill Smelkov * return control of the port to the EHCI controller.
364c67045bSKirill Smelkov */
companion_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)37*ed5bd7a4SGreg Kroah-Hartman static ssize_t companion_store(struct device *dev,
384c67045bSKirill Smelkov struct device_attribute *attr,
394c67045bSKirill Smelkov const char *buf, size_t count)
404c67045bSKirill Smelkov {
414c67045bSKirill Smelkov struct ehci_hcd *ehci;
424c67045bSKirill Smelkov int portnum, new_owner;
434c67045bSKirill Smelkov
440521cfd0SPeter Chen ehci = hcd_to_ehci(dev_get_drvdata(dev));
454c67045bSKirill Smelkov new_owner = PORT_OWNER; /* Owned by companion */
464c67045bSKirill Smelkov if (sscanf(buf, "%d", &portnum) != 1)
474c67045bSKirill Smelkov return -EINVAL;
484c67045bSKirill Smelkov if (portnum < 0) {
494c67045bSKirill Smelkov portnum = - portnum;
504c67045bSKirill Smelkov new_owner = 0; /* Owned by EHCI */
514c67045bSKirill Smelkov }
524c67045bSKirill Smelkov if (portnum <= 0 || portnum > HCS_N_PORTS(ehci->hcs_params))
534c67045bSKirill Smelkov return -ENOENT;
544c67045bSKirill Smelkov portnum--;
554c67045bSKirill Smelkov if (new_owner)
564c67045bSKirill Smelkov set_bit(portnum, &ehci->companion_ports);
574c67045bSKirill Smelkov else
584c67045bSKirill Smelkov clear_bit(portnum, &ehci->companion_ports);
594c67045bSKirill Smelkov set_owner(ehci, portnum, new_owner);
604c67045bSKirill Smelkov return count;
614c67045bSKirill Smelkov }
62*ed5bd7a4SGreg Kroah-Hartman static DEVICE_ATTR_RW(companion);
634c67045bSKirill Smelkov
64cc62a7ebSKirill Smelkov
65cc62a7ebSKirill Smelkov /*
66cc62a7ebSKirill Smelkov * Display / Set uframe_periodic_max
67cc62a7ebSKirill Smelkov */
uframe_periodic_max_show(struct device * dev,struct device_attribute * attr,char * buf)68*ed5bd7a4SGreg Kroah-Hartman static ssize_t uframe_periodic_max_show(struct device *dev,
69cc62a7ebSKirill Smelkov struct device_attribute *attr,
70cc62a7ebSKirill Smelkov char *buf)
71cc62a7ebSKirill Smelkov {
72cc62a7ebSKirill Smelkov struct ehci_hcd *ehci;
73cc62a7ebSKirill Smelkov int n;
74cc62a7ebSKirill Smelkov
750521cfd0SPeter Chen ehci = hcd_to_ehci(dev_get_drvdata(dev));
76cc62a7ebSKirill Smelkov n = scnprintf(buf, PAGE_SIZE, "%d\n", ehci->uframe_periodic_max);
77cc62a7ebSKirill Smelkov return n;
78cc62a7ebSKirill Smelkov }
79cc62a7ebSKirill Smelkov
80cc62a7ebSKirill Smelkov
uframe_periodic_max_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)81*ed5bd7a4SGreg Kroah-Hartman static ssize_t uframe_periodic_max_store(struct device *dev,
82cc62a7ebSKirill Smelkov struct device_attribute *attr,
83cc62a7ebSKirill Smelkov const char *buf, size_t count)
84cc62a7ebSKirill Smelkov {
85cc62a7ebSKirill Smelkov struct ehci_hcd *ehci;
86cc62a7ebSKirill Smelkov unsigned uframe_periodic_max;
87d0ce5c6bSAlan Stern unsigned uframe;
88cc62a7ebSKirill Smelkov unsigned long flags;
89cc62a7ebSKirill Smelkov ssize_t ret;
90cc62a7ebSKirill Smelkov
910521cfd0SPeter Chen ehci = hcd_to_ehci(dev_get_drvdata(dev));
92cc62a7ebSKirill Smelkov if (kstrtouint(buf, 0, &uframe_periodic_max) < 0)
93cc62a7ebSKirill Smelkov return -EINVAL;
94cc62a7ebSKirill Smelkov
95cc62a7ebSKirill Smelkov if (uframe_periodic_max < 100 || uframe_periodic_max >= 125) {
96cc62a7ebSKirill Smelkov ehci_info(ehci, "rejecting invalid request for "
97cc62a7ebSKirill Smelkov "uframe_periodic_max=%u\n", uframe_periodic_max);
98cc62a7ebSKirill Smelkov return -EINVAL;
99cc62a7ebSKirill Smelkov }
100cc62a7ebSKirill Smelkov
101cc62a7ebSKirill Smelkov ret = -EINVAL;
102cc62a7ebSKirill Smelkov
103cc62a7ebSKirill Smelkov /*
104cc62a7ebSKirill Smelkov * lock, so that our checking does not race with possible periodic
105cc62a7ebSKirill Smelkov * bandwidth allocation through submitting new urbs.
106cc62a7ebSKirill Smelkov */
107cc62a7ebSKirill Smelkov spin_lock_irqsave (&ehci->lock, flags);
108cc62a7ebSKirill Smelkov
109cc62a7ebSKirill Smelkov /*
110cc62a7ebSKirill Smelkov * for request to decrease max periodic bandwidth, we have to check
111d0ce5c6bSAlan Stern * to see whether the decrease is possible.
112cc62a7ebSKirill Smelkov */
113cc62a7ebSKirill Smelkov if (uframe_periodic_max < ehci->uframe_periodic_max) {
114d0ce5c6bSAlan Stern u8 allocated_max = 0;
115cc62a7ebSKirill Smelkov
116d0ce5c6bSAlan Stern for (uframe = 0; uframe < EHCI_BANDWIDTH_SIZE; ++uframe)
117cc62a7ebSKirill Smelkov allocated_max = max(allocated_max,
118d0ce5c6bSAlan Stern ehci->bandwidth[uframe]);
119cc62a7ebSKirill Smelkov
120cc62a7ebSKirill Smelkov if (allocated_max > uframe_periodic_max) {
121cc62a7ebSKirill Smelkov ehci_info(ehci,
1226774def6SMasanari Iida "cannot decrease uframe_periodic_max because "
123cc62a7ebSKirill Smelkov "periodic bandwidth is already allocated "
124cc62a7ebSKirill Smelkov "(%u > %u)\n",
125cc62a7ebSKirill Smelkov allocated_max, uframe_periodic_max);
126cc62a7ebSKirill Smelkov goto out_unlock;
127cc62a7ebSKirill Smelkov }
128cc62a7ebSKirill Smelkov }
129cc62a7ebSKirill Smelkov
130cc62a7ebSKirill Smelkov /* increasing is always ok */
131cc62a7ebSKirill Smelkov
132cc62a7ebSKirill Smelkov ehci_info(ehci, "setting max periodic bandwidth to %u%% "
133cc62a7ebSKirill Smelkov "(== %u usec/uframe)\n",
134cc62a7ebSKirill Smelkov 100*uframe_periodic_max/125, uframe_periodic_max);
135cc62a7ebSKirill Smelkov
136cc62a7ebSKirill Smelkov if (uframe_periodic_max != 100)
137cc62a7ebSKirill Smelkov ehci_warn(ehci, "max periodic bandwidth set is non-standard\n");
138cc62a7ebSKirill Smelkov
139cc62a7ebSKirill Smelkov ehci->uframe_periodic_max = uframe_periodic_max;
140cc62a7ebSKirill Smelkov ret = count;
141cc62a7ebSKirill Smelkov
142cc62a7ebSKirill Smelkov out_unlock:
143cc62a7ebSKirill Smelkov spin_unlock_irqrestore (&ehci->lock, flags);
144cc62a7ebSKirill Smelkov return ret;
145cc62a7ebSKirill Smelkov }
146*ed5bd7a4SGreg Kroah-Hartman static DEVICE_ATTR_RW(uframe_periodic_max);
147cc62a7ebSKirill Smelkov
148cc62a7ebSKirill Smelkov
create_sysfs_files(struct ehci_hcd * ehci)1494c67045bSKirill Smelkov static inline int create_sysfs_files(struct ehci_hcd *ehci)
1504c67045bSKirill Smelkov {
151cc62a7ebSKirill Smelkov struct device *controller = ehci_to_hcd(ehci)->self.controller;
1524c67045bSKirill Smelkov int i = 0;
1534c67045bSKirill Smelkov
1544c67045bSKirill Smelkov /* with integrated TT there is no companion! */
1554c67045bSKirill Smelkov if (!ehci_is_TDI(ehci))
156cc62a7ebSKirill Smelkov i = device_create_file(controller, &dev_attr_companion);
157cc62a7ebSKirill Smelkov if (i)
158cc62a7ebSKirill Smelkov goto out;
159cc62a7ebSKirill Smelkov
160cc62a7ebSKirill Smelkov i = device_create_file(controller, &dev_attr_uframe_periodic_max);
161cc62a7ebSKirill Smelkov out:
1624c67045bSKirill Smelkov return i;
1634c67045bSKirill Smelkov }
1644c67045bSKirill Smelkov
remove_sysfs_files(struct ehci_hcd * ehci)1654c67045bSKirill Smelkov static inline void remove_sysfs_files(struct ehci_hcd *ehci)
1664c67045bSKirill Smelkov {
167cc62a7ebSKirill Smelkov struct device *controller = ehci_to_hcd(ehci)->self.controller;
168cc62a7ebSKirill Smelkov
1694c67045bSKirill Smelkov /* with integrated TT there is no companion! */
1704c67045bSKirill Smelkov if (!ehci_is_TDI(ehci))
171cc62a7ebSKirill Smelkov device_remove_file(controller, &dev_attr_companion);
172cc62a7ebSKirill Smelkov
173cc62a7ebSKirill Smelkov device_remove_file(controller, &dev_attr_uframe_periodic_max);
1744c67045bSKirill Smelkov }
175