1 # --- T2-COPYRIGHT-NOTE-BEGIN ---
2 # T2 SDE: package/*/linux/xserve-frontpanel.patch
3 # Copyright (C) 2024 The T2 SDE Project
5 # This Copyright note is generated by scripts/Create-CopyPatch,
6 # more information can be found in the files COPYING and README.
8 # This patch file is dual-licensed. It is available under the license the
9 # patched project is licensed under, as long as it is an OpenSource license
10 # as defined at http://www.opensource.org/ (e.g. BSD, X11) or under the terms
11 # of the GNU General Public License version 2 as used by the T2 SDE.
12 # --- T2-COPYRIGHT-NOTE-END ---
14 This adds a new driver to monitor the cpu load and control the Apple
15 Xserve Intel Xeon server front-panel LEDs.
17 Signed-off-by: René Rebe <rene@exactcode.com>
19 --- linux-6.12/drivers/usb/misc/Kconfig.vanilla 2024-11-18 14:19:24.486102061 +0100
20 +++ linux-6.12/drivers/usb/misc/Kconfig 2024-11-18 14:19:49.486102006 +0100
22 during hub start-up configuration stage. It is must to enable this
23 option on AMD Kria KR260 Robotics Starter Kit as this hub is
24 connected to USB-SD converter which mounts the root filesystem.
26 +config USB_XSERVE_FRONTPANEL
27 + tristate "Apple Xserve frontpanel support"
30 + Say Y here if you want to control the frontpanel of Apple Xserve
32 --- linux-6.8/drivers/usb/misc/Makefile.vanilla 2024-05-09 18:36:44.386205674 +0200
33 +++ linux-6.8/drivers/usb/misc/Makefile 2024-05-09 18:37:25.977208996 +0200
35 obj-$(CONFIG_USB_HSIC_USB3503) += usb3503.o
36 obj-$(CONFIG_USB_HSIC_USB4604) += usb4604.o
37 obj-$(CONFIG_USB_CHAOSKEY) += chaoskey.o
38 +obj-$(CONFIG_USB_XSERVE_FRONTPANEL) += xserve-frontpanel.o
40 obj-$(CONFIG_USB_SISUSBVGA) += sisusbvga/
41 obj-$(CONFIG_USB_LINK_LAYER_TEST) += lvstest.o
42 --- /dev/null 2024-04-27 11:06:15.379862889 +0200
43 +++ linux-6.8/drivers/usb/misc/xserve-frontpanel.c 2024-05-12 18:31:15.538230373 +0200
45 +// SPDX-License-Identifier: GPL-2.0
47 + * Apple Xserve USB front-panel driver.
48 + * Copyright (C) 2024 René Rebe <rene@exactcode.com>
50 + * based on RackMac vu-meter driver
51 + * (c) Copyright 2006 Benjamin Herrenschmidt, IBM Corp.
52 + * <benh@kernel.crashing.org>
54 + * based on USB Skeleton driver - 2.2
55 + * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com)
61 +#include <linux/kernel.h>
62 +#include <linux/errno.h>
63 +#include <linux/slab.h>
64 +#include <linux/module.h>
65 +#include <linux/kref.h>
66 +#include <linux/uaccess.h>
67 +#include <linux/usb.h>
68 +#include <linux/mutex.h>
69 +#include <linux/cpufreq.h>
71 +#define PANEL_VENDOR 0x5ac
72 +#define PANEL_PRODUCT 0x8261
73 +#define PANEL_CONFIG 0
74 +#define PANEL_DATA_SIZE 32
76 +/* CPU meter sampling rate in ms */
77 +#define CPU_SAMPLING_RATE 250
80 +/* table of devices that work with this driver */
81 +static const struct usb_device_id frontpanel_table[] = {
82 + { USB_DEVICE(PANEL_VENDOR, PANEL_PRODUCT) },
83 + { } /* Terminating entry */
85 +MODULE_DEVICE_TABLE(usb, frontpanel_table);
89 + * MAX_TRANSFER is chosen so that the VM is not stressed by
90 + * allocations > PAGE_SIZE and the number of packets in a page
91 + * is an integer 512 is the largest possible packet on EHCI
93 +#define WRITES_IN_FLIGHT 8
94 +/* arbitrarily chosen */
97 +struct rackmeter_cpu {
103 +/* Structure to hold all of our device specific stuff */
104 +struct usb_frontpanel {
105 + struct usb_device *udev; /* the usb device for this device */
106 + struct usb_interface *interface; /* the interface for this device */
107 + struct semaphore limit_sem; /* limiting the number of writes in progress */
108 + struct usb_anchor submitted; /* in case we need to retract our submissions */
109 + int errors; /* the last request tanked */
110 + spinlock_t err_lock; /* lock for errors */
112 + struct mutex io_mutex; /* synchronize I/O with disconnect */
113 + __u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */
114 + unsigned long disconnected:1;
116 + struct delayed_work sniffer;
118 + __u8 buffer[PANEL_DATA_SIZE];
119 + struct rackmeter_cpu cpu[16];
121 +#define to_fp_dev(d) container_of(d, struct usb_frontpanel, kref)
123 +static void frontpanel_draw_down(struct usb_frontpanel *dev);
125 +static void frontpanel_delete(struct kref *kref)
127 + struct usb_frontpanel *dev = to_fp_dev(kref);
129 + usb_put_intf(dev->interface);
130 + usb_put_dev(dev->udev);
134 +static void frontpanel_write_bulk_callback(struct urb *urb)
136 + struct usb_frontpanel *dev;
137 + unsigned long flags;
139 + dev = urb->context;
141 + /* sync/async unlink faults aren't errors */
143 + if (!(urb->status == -ENOENT ||
144 + urb->status == -ECONNRESET ||
145 + urb->status == -ESHUTDOWN))
146 + dev_err(&dev->interface->dev,
147 + "%s - nonzero write bulk status received: %d\n",
148 + __func__, urb->status);
150 + spin_lock_irqsave(&dev->err_lock, flags);
151 + dev->errors = urb->status;
152 + spin_unlock_irqrestore(&dev->err_lock, flags);
155 + /* free up our allocated buffer */
156 + usb_free_coherent(urb->dev, urb->transfer_buffer_length,
157 + urb->transfer_buffer, urb->transfer_dma);
158 + up(&dev->limit_sem);
161 +static ssize_t frontpanel_write(struct usb_frontpanel *dev, const char *buffer, size_t count)
164 + struct urb *urb = NULL;
166 + size_t writesize = min_t(size_t, count, PANEL_DATA_SIZE);
169 + * limit the number of URBs in flight to stop a user from using up all
172 + if (down_trylock(&dev->limit_sem)) {
177 + spin_lock_irq(&dev->err_lock);
178 + retval = dev->errors;
180 + /* any error is reported once */
182 + /* to preserve notifications about reset */
183 + retval = (retval == -EPIPE) ? retval : -EIO;
185 + spin_unlock_irq(&dev->err_lock);
189 + /* create a urb, and a buffer for it, and copy the data to the urb */
190 + urb = usb_alloc_urb(0, GFP_KERNEL);
196 + buf = usb_alloc_coherent(dev->udev, writesize, GFP_KERNEL,
197 + &urb->transfer_dma);
203 + memcpy(buf, buffer, writesize);
205 + /* this lock makes sure we don't submit URBs to gone devices */
206 + mutex_lock(&dev->io_mutex);
207 + if (dev->disconnected) { /* disconnect() was called */
208 + mutex_unlock(&dev->io_mutex);
213 + /* initialize the urb properly */
214 + usb_fill_bulk_urb(urb, dev->udev,
215 + usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),
216 + buf, writesize, frontpanel_write_bulk_callback, dev);
217 + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
218 + usb_anchor_urb(urb, &dev->submitted);
220 + /* send the data out the bulk port */
221 + retval = usb_submit_urb(urb, GFP_KERNEL);
222 + mutex_unlock(&dev->io_mutex);
224 + dev_err(&dev->interface->dev,
225 + "%s - failed submitting write urb, error %d\n",
227 + goto error_unanchor;
231 + * release our reference to this urb, the USB core will eventually free
239 + usb_unanchor_urb(urb);
242 + usb_free_coherent(dev->udev, writesize, buf, urb->transfer_dma);
245 + up(&dev->limit_sem);
251 +static void rackmeter_do_timer(struct work_struct *work)
253 + struct usb_frontpanel *dev = container_of(work, struct usb_frontpanel, sniffer.work);
255 + unsigned int load, cpu, updated = 0;
256 + u64 cpu_idle, cpu_wall;
257 + s64 diff_idle, diff_wall;
260 + for_each_online_cpu(cpu) {
261 + struct rackmeter_cpu *rcpu;
264 + rcpu = &dev->cpu[cpu];
266 + cpu_idle = get_cpu_idle_time(cpu, &cpu_wall, 0);
267 + diff_idle = cpu_idle - rcpu->prev_idle;
268 + diff_wall = cpu_wall - rcpu->prev_wall;
269 + if (diff_idle > diff_wall)
270 + diff_wall = diff_idle;
272 + /* We do a very dumb calculation to update the LEDs for now */
273 + load = div64_u64(255 * (diff_wall - diff_idle), diff_wall);
275 + if (dev->buffer[cpu] != (__u8)load) {
276 + dev->buffer[cpu] = (__u8)load;
280 + rcpu->prev_idle = cpu_idle;
281 + rcpu->prev_wall = cpu_wall;
285 + ret = frontpanel_write(dev, dev->buffer, PANEL_DATA_SIZE);
287 + dev_err(&dev->interface->dev, "write failed: %ld\n", ret);
290 + schedule_delayed_work_on(smp_processor_id(), &dev->sniffer, msecs_to_jiffies(CPU_SAMPLING_RATE));
293 +static void rackmeter_init_cpu_sniffer(struct usb_frontpanel *dev)
297 + INIT_DELAYED_WORK(&dev->sniffer, rackmeter_do_timer);
299 + for_each_online_cpu(cpu) {
300 + struct rackmeter_cpu *rcpu;
303 + rcpu = &dev->cpu[cpu];
305 + rcpu->prev_wall = 0;
306 + rcpu->prev_idle = 0;
309 + schedule_delayed_work_on(smp_processor_id(), &dev->sniffer, msecs_to_jiffies(CPU_SAMPLING_RATE));
313 +static void rackmeter_stop_cpu_sniffer(struct usb_frontpanel *dev)
315 + cancel_delayed_work_sync(&dev->sniffer);
318 +static int frontpanel_probe(struct usb_interface *interface,
319 + const struct usb_device_id *id)
321 + struct usb_frontpanel *dev;
322 + struct usb_endpoint_descriptor *bulk_out;
325 + /* allocate memory for our device state and initialize it */
326 + dev = kzalloc(sizeof(*dev), GFP_KERNEL);
330 + kref_init(&dev->kref);
331 + sema_init(&dev->limit_sem, WRITES_IN_FLIGHT);
332 + mutex_init(&dev->io_mutex);
333 + spin_lock_init(&dev->err_lock);
334 + init_usb_anchor(&dev->submitted);
336 + dev->udev = usb_get_dev(interface_to_usbdev(interface));
337 + dev->interface = usb_get_intf(interface);
339 + /* set up the endpoint information */
340 + /* use only the first bulk-in and bulk-out endpoints */
341 + retval = usb_find_common_endpoints(interface->cur_altsetting,
342 + NULL, &bulk_out, NULL, NULL);
344 + dev_err(&interface->dev,
345 + "Could not find bulk-out endpoints\n");
349 + dev->bulk_out_endpointAddr = bulk_out->bEndpointAddress;
351 + /* save our data pointer in this interface device */
352 + usb_set_intfdata(interface, dev);
354 + rackmeter_init_cpu_sniffer(dev);
359 + /* this frees allocated memory */
360 + kref_put(&dev->kref, frontpanel_delete);
365 +static void frontpanel_disconnect(struct usb_interface *interface)
367 + struct usb_frontpanel *dev;
368 + dev = usb_get_intfdata(interface);
370 + rackmeter_stop_cpu_sniffer(dev);
372 + /* prevent more I/O from starting */
373 + mutex_lock(&dev->io_mutex);
374 + dev->disconnected = 1;
375 + mutex_unlock(&dev->io_mutex);
377 + usb_kill_anchored_urbs(&dev->submitted);
379 + /* decrement our usage count */
380 + kref_put(&dev->kref, frontpanel_delete);
383 +static void frontpanel_draw_down(struct usb_frontpanel *dev)
387 + time = usb_wait_anchor_empty_timeout(&dev->submitted, 1000);
389 + usb_kill_anchored_urbs(&dev->submitted);
392 +static int frontpanel_suspend(struct usb_interface *intf, pm_message_t message)
394 + struct usb_frontpanel *dev = usb_get_intfdata(intf);
398 + frontpanel_draw_down(dev);
402 +static int frontpanel_resume(struct usb_interface *intf)
407 +static int frontpanel_pre_reset(struct usb_interface *intf)
409 + struct usb_frontpanel *dev = usb_get_intfdata(intf);
411 + mutex_lock(&dev->io_mutex);
412 + frontpanel_draw_down(dev);
417 +static int frontpanel_post_reset(struct usb_interface *intf)
419 + struct usb_frontpanel *dev = usb_get_intfdata(intf);
421 + /* we are sure no URBs are active - no locking needed */
422 + dev->errors = -EPIPE;
423 + mutex_unlock(&dev->io_mutex);
428 +static struct usb_driver frontpanel_driver = {
429 + .name = "xserve-frontpanel",
430 + .probe = frontpanel_probe,
431 + .disconnect = frontpanel_disconnect,
432 + .suspend = frontpanel_suspend,
433 + .resume = frontpanel_resume,
434 + .pre_reset = frontpanel_pre_reset,
435 + .post_reset = frontpanel_post_reset,
436 + .id_table = frontpanel_table,
437 + /*.supports_autosuspend = 1,*/
440 +module_usb_driver(frontpanel_driver);
442 +MODULE_AUTHOR("René Rebe");
443 +MODULE_DESCRIPTION("Apple Xserve USB front-panel driver");
444 +MODULE_LICENSE("GPL v2");