blob: 3b27cc5485378cd379ee0e0dee47d7f61e0f08ad [file] [log] [blame]
Kyle Swenson7900a3c2021-08-12 14:34:44 -06001/*
2 * ./kernel_modules/pse/pse-class.c
3 *
4 * This file defines a device class for 802.3af/at/at+ PSE devices.
5 *
6 * Author: Cradlepoint Technology, Inc. <source@cradlepoint.com>
7 * Kyle Swenson <kswenson@cradlepoint.com>
8 *
9 * Copyright 2017 Cradlepoint Technology, Inc.
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2
13 * as published by the Free Software Foundation.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 */
21#include <linux/kernel.h>
22#include <linux/module.h>
23#include <linux/device.h>
24#include <linux/err.h>
25#include <linux/of.h>
26#include <linux/slab.h>
27#include <linux/sysfs.h>
28#include <linux/idr.h>
29#include <linux/spinlock.h>
30#include <linux/workqueue.h>
31#include <linux/list_sort.h>
32#include <linux/sort.h>
33#include "pse-port.h"
34#include "pse-sysfs.h"
35#include "pse-class.h"
36
37#define DEFSTRLEN 32
38
39static int determine_power_used(struct class *class, struct device *pdev);
40static int power_port(struct device *dev);
41static int request_power(struct device *dev);
42static void pse_port_power_request_handler(struct work_struct *work);
43static void pse_port_power_disconnect_handler(struct work_struct *work);
44
45
46
47static struct class *g_pse_class;
48static DEFINE_IDA(pse_port_ida);
49
50
51/* match_port_priority: Return the first pse_port_device that has a priority
52 * equal the priority passed in via data
53 */
54int match_port_priority(struct device *dev, const void *data)
55{
56 uint8_t priority;
57
58 if (data) {
59 priority = *((uint8_t *)data);
60 if (to_pse_port_device(dev)->priority == priority)
61 return 1;/*Match found */
62 }
63 return 0; /* no match */
64}
65
66
67int match_port_id(struct device *dev, const void *data)
68{
69 uint8_t port_id;
70
71 if (data) {
72 port_id = *((uint8_t *)data);
73 if (to_ppd(dev)->id == port_id)
74 return 1;/*Match found */
75 }
76 return 0; /* no match */
77}
78
79
80/* compare_port_priority: if p1 is a lower priority (which means a higher value
81 * in port->priority) port than p2, return -1 If the ports have the same
82 * priority, then look at port->id, lower id, higher priority
83 *
84 * P1 : priority 1
85 * P2 : priority 3
86 * P1 has a higher priority than P2
87 * compare_port_priority(P1, P2) -> -1
88 * compare_port_priority(P1, P1) -> 0
89 * compare_port_priority(P2, P1) -> 1
90 *
91 *
92 */
93
94int compare_port_priority(struct pse_port_device *p1, struct pse_port_device *p2)
95{
96 if (p1->priority > p2->priority)
97 return -1;
98 if (p1->priority < p2->priority)
99 return 1;
100
101 /* (p1->priority == p2->priority) */
102 if (p1->id > p2->id)
103 return -1;
104 if (p1->id < p2->id)
105 return 1;
106
107 /* (p1->priority == p2->priority and p1->id == p2->id) */
108 return 0;
109}
110
111#define LT(p1, p2) (compare_port_priority((p1), (p2)) < 0)
112#define GT(p1, p2) (compare_port_priority((p1), (p2)) > 0)
113#define EQ(p1, p2) (compare_port_priority((p1), (p2)) == 0)
114#define LEQ(p1, p2) (!GT((p1), (p2)))
115#define GEQ(p1, p2) (!LT((p1), (p2)))
116
117struct device *get_highest_priority_device(struct class *class)
118{
119 /* Caller is responsible for calling put_device */
120 struct class_dev_iter iter;
121 struct device *dev;
122 struct device *hpdev;
123
124 class_dev_iter_init(&iter, class, NULL, NULL);
125 dev = class_dev_iter_next(&iter);
126 hpdev = get_device(dev);
127 while (dev) {
128 if (GT(to_ppd(dev), to_ppd(hpdev))) {
129 put_device(hpdev);
130 hpdev = get_device(dev);
131 }
132 dev = class_dev_iter_next(&iter);
133 }
134 class_dev_iter_exit(&iter);
135 return hpdev;
136}
137EXPORT_SYMBOL(get_highest_priority_device);
138
139struct device *get_lowest_priority_device(struct class *class)
140{
141 struct class_dev_iter iter;
142 struct device *dev;
143 struct device *lpdev;
144
145 class_dev_iter_init(&iter, class, NULL, NULL);
146 dev = class_dev_iter_next(&iter);
147 lpdev = get_device(dev);
148 while (dev) {
149 if (LT(to_ppd(dev), to_ppd(lpdev))) {
150 put_device(lpdev);
151 lpdev = get_device(dev);
152 }
153 dev = class_dev_iter_next(&iter);
154 }
155 class_dev_iter_exit(&iter);
156 return lpdev;
157}
158struct device *get_next_lowest_priority_device(struct class *class,
159 struct device *compare_dev)
160{
161 struct class_dev_iter iter;
162 struct device *current_dev;
163 struct device *next_lowest_dev;
164
165 dev_dbg(compare_dev, "Next_lowest_priority_device, finding next dev after %d %d",
166 to_ppd(compare_dev)->id, to_ppd(compare_dev)->priority);
167
168 class_dev_iter_init(&iter, class, NULL, NULL);
169 current_dev = class_dev_iter_next(&iter);
170 next_lowest_dev = NULL;
171 while (current_dev) {
172 if (LT(to_ppd(current_dev), to_ppd(compare_dev))) {
173 if (!next_lowest_dev)
174 next_lowest_dev = current_dev;
175
176 if (GEQ(to_ppd(current_dev), to_ppd(next_lowest_dev))) {
177 next_lowest_dev = get_device(current_dev);
178 dev_dbg(next_lowest_dev, "is the next lowest priority device");
179 }
180 }
181 current_dev = class_dev_iter_next(&iter);
182 }
183 class_dev_iter_exit(&iter);
184 return next_lowest_dev;
185}
186EXPORT_SYMBOL(get_next_lowest_priority_device);
187
188
189struct device *relink_priority_list(struct class *class)
190{
191 struct device *headdev;
192 struct device *dev;
193 struct device *ndev;
194
195 headdev = get_highest_priority_device(class);
196 if (!headdev)
197 return NULL;
198
199 dev = headdev;
200 to_ppd(dev)->next_hp = NULL;
201 ndev = get_next_lowest_priority_device(class, dev);
202 while (ndev) {
203 to_ppd(dev)->next_lp = ndev;
204 to_ppd(ndev)->next_hp = dev;
205 dev = ndev;
206 ndev = get_next_lowest_priority_device(class, dev);
207 }
208 to_ppd(dev)->next_lp = NULL;
209 return headdev;
210}
211
212
213/* Sums the power used by pdev and all lower-priority devices. To get the total power used, set pdev to NULL */
214static int determine_power_used(struct class *class, struct device *pdev)
215{
216 int power_used = 0;
217 struct pse_port_device *port;
218
219 if (pdev == NULL)
220 pdev = get_highest_priority_device(class);
221
222 while (pdev) {
223 port = to_ppd(pdev);
224 power_used += port->power_allocation;
225 pdev = get_next_lowest_priority_device(class, &port->dev);
226 }
227 return power_used;
228}
229
230static int power_port(struct device *dev)
231{
232 struct pse_data *pse_data = get_pse_data(dev->class);
233 struct pse_port_device *port = to_pse_port_device(dev);
234 uint16_t port_class = port->last_class_result;
235
236 if (port_class >= CLASS_INVALID) {
237 dev_warn(dev, "Attempt to power a port with an invalid class\n");
238 return -EINVAL;
239 }
Kyle Swenson7900a3c2021-08-12 14:34:44 -0600240
Kyle Swensond6f4dd02022-04-16 22:55:40 -0600241 if (port->power_allocation == 0)
242 port->ops->apply_power(&port->dev, 1);
243
244 /* New power used is the difference between what we were allocated, and what we're requesting */
245 pse_data->power_used += port->power_request - port->power_allocation;
246
247 port->power_allocation = port->power_request;
248 port->ops->set_power_limit(&port->dev, port->power_allocation);
Kyle Swenson7900a3c2021-08-12 14:34:44 -0600249 return 0;
250}
251
252void disconnect_port(struct device *dev)
253{
254 struct pse_port_device *port = to_pse_port_device(dev);
255 struct pse_data *pse_data = get_pse_data(dev->class);
256
257 if (port->power_allocation > 0)
258 pse_data->power_used -= port->power_allocation;
Kyle Swensond6f4dd02022-04-16 22:55:40 -0600259 port->power_allocation = 0;
260 port->power_request = 0;
Kyle Swenson7900a3c2021-08-12 14:34:44 -0600261 port->ops->reset(&port->dev);
262}
263
264/* request_power - potentially power a port. This function really needs to be
265 * called in port-priority order. If this is not called in priority order then
266 * when the get glitching, where a lower-priority port will be powered briefly
267 * until the higher-priority port port requesting the power has a higher
268 * priority than ports that are currently powered, those will likely be
269 * unpowered. NOTE: power_request should be a positive number indicating total
270 * power wanted.
271 */
272static int request_power(struct device *dev)
273{
274 int power_remaining;
275 int lpu = 0;
276 int32_t power_request;
277 struct device *lpdev;
278 struct pse_data *pse_data = get_pse_data(dev->class);
279 struct pse_port_device *port = to_pse_port_device(dev);
280
281 dev_dbg(&port->dev, "Request_power begin:");
282 if (port->last_class_result >= CLASS_INVALID) {
283 dev_warn(&port->dev, "request_power called with invalid port classification result");
284 port->ops->reset(&port->dev);
285 return -EINVAL;
286 }
Kyle Swenson7900a3c2021-08-12 14:34:44 -0600287
Kyle Swensond6f4dd02022-04-16 22:55:40 -0600288
289 power_request = port->power_request - port->power_allocation;
Kyle Swenson7900a3c2021-08-12 14:34:44 -0600290 power_remaining = ((int32_t) pse_data->power_budget) - ((int32_t) pse_data->power_used);
291 dev_dbg(&port->dev, "Is requesting %i mW", power_request);
292 dev_dbg(&port->dev, "We have %i mW remaining in our budget", power_remaining);
293
294 if (power_remaining >= power_request) {
295 dev_info(&port->dev, "Powering port%d because: power_budget (%i) - power_used (%i) - power_requested (%i) >= 0", port->id, pse_data->power_budget, pse_data->power_used, power_request);
296 return power_port(&port->dev);
297 }
298
299 /* If the total current potentially gained by unpowering all the ports _in
300 * addition to the power currently remaining_ will not give enough current
301 * to power this device, there's no point to unpowering all the other ports
302 */
303
304 lpu = determine_power_used(&pse_data->cls, &port->dev);
305 if ((power_remaining + lpu) < power_request) {
306 dev_dbg(&port->dev, " No budget left, power used by lower-priority devices is %i, power requested is %i", lpu, power_request);
307 return -EBUSY;
308 }
309
310 dev_info(&port->dev, "power_remaining (%i) + power used by lower priority devices (%i) will net us %i enough to power this request of %i", power_remaining, lpu, power_remaining+lpu, power_request);
311
312 lpdev = get_lowest_priority_device(&pse_data->cls);
313 while (lpdev && (power_remaining < power_request)) {
314 if (to_ppd(lpdev)->power_allocation > 0) {
315 dev_info(&port->dev, "port%d (priority %d) is being unpowered to power port%d (priority %d)", to_ppd(lpdev)->id, to_ppd(lpdev)->priority, port->id, port->priority);
316 disconnect_port(lpdev);
317 power_remaining = pse_data->power_budget - pse_data->power_used;
318 }
319 lpdev = to_ppd(lpdev)->next_hp;
320 }
321
322 if (power_remaining >= power_request) {
323 dev_info(&port->dev, "Powering port%d, after powering off lower priority ports\n", port->id);
324 return power_port(&port->dev);
325 }
326
327 /* If we get here, then our algorithm is wrong or state is corrupt */
328 dev_info(&port->dev, "Warning, power_remaining (%i) < power_request (%i), even after unpowering devices", power_remaining, power_request);
329 return -EAGAIN;
330}
331
332/* This is called in response to userspace writing to pse/port_priority, or
333 * pse/power_budget
334 */
335void redistribute_power(struct class *class)
336{
337 struct device *h;
338 struct pse_port_device *p;
339
340 h = get_highest_priority_device(class);
341 while (h) {
342 p = to_ppd(h);
343 mutex_lock(&p->lock);
344 queue_work(p->wq, &p->disconnect);
345 mutex_unlock(&p->lock);
346 h = get_next_lowest_priority_device(class, h);
347 }
348}
349static void pse_port_power_request_handler(struct work_struct *work)
350{
351 struct pse_port_device *port = container_of(work, struct pse_port_device, request_power);
352 struct pse_data *pse_data = get_pse_data(port->dev.class);
353
354 mutex_lock(&pse_data->pse_lock);
355 dev_dbg(&port->dev, "in %s\n", __func__);
356 if (!pse_data->enabled) {
357 /* No powering devices if we're disabled */
358 goto unlock_class_exit;
359 }
360
361 mutex_lock(&port->lock);
362 if (!port->enabled) {
363 /* No powering devices if the port is disabled */
364 goto unlock_port_exit;
365 }
366
Kyle Swensond6f4dd02022-04-16 22:55:40 -0600367 if (port->power_request == port->power_allocation) {
Kyle Swenson7900a3c2021-08-12 14:34:44 -0600368 /* If the port is already powered, don't try to power it again*/
369 goto unlock_port_exit;
370 }
371
372 if (port->last_class_result != CLASS_INVALID && port->last_detect_result == DETECT_POWERED_DEVICE) {
373 if (request_power(&port->dev) == 0)
374 dev_info(&port->dev, "Has been allocated %i mW, and will be powered", port->power_allocation);
375 }
376
377unlock_port_exit:
378 mutex_unlock(&port->lock);
379unlock_class_exit:
380 mutex_unlock(&pse_data->pse_lock);
381
382}
383
384static void pse_port_power_disconnect_handler(struct work_struct *work)
385{
386 struct pse_port_device *p = container_of(work, struct pse_port_device, disconnect);
387 struct pse_data *pse_data = get_pse_data(p->dev.class);
388
389 mutex_lock(&pse_data->pse_lock);
390 disconnect_port(&p->dev);
391 mutex_unlock(&pse_data->pse_lock);
392}
393
394
395static void pse_port_device_release(struct device *dev)
396{
397 struct pse_port_device *port = to_ppd(dev);
398 struct pse_data *pse_data = get_pse_data(dev->class);
399
400 mutex_lock(&pse_data->pse_lock);
401 dev_dbg(dev, "%s:%d\n", __func__, __LINE__);
402 ida_simple_remove(&pse_port_ida, port->id);
403 kfree(port);
404 mutex_unlock(&pse_data->pse_lock);
405
406}
407
408
409struct pse_port_device *pse_port_device_create(struct device *dev, const char *name, const struct pse_port_ops *ops, struct module *owner)
410{
411
412 struct pse_port_device *pse_port;
413
414 pse_port = devm_kzalloc(dev, sizeof(struct pse_port_device), GFP_KERNEL);
415 if (pse_port == NULL)
416 return NULL;
417
418 pse_port->ops = ops;
419 pse_port->owner = owner;
420 pse_port->dev.parent = dev;
421 strlcpy(pse_port->name, name, PSE_PORT_NAME_SIZE);
422 return pse_port_device_register(pse_port);
423
424
425}
426EXPORT_SYMBOL(pse_port_device_create);
427
428struct pse_port_device *pse_port_device_register(struct pse_port_device *pse_port)
429{
430 int err;
431 int id = -1;
432 struct pse_data *pse_data = get_pse_data(g_pse_class);
433
434 if (id < 0) {
435 id = ida_simple_get(&pse_port_ida, 0, 0, GFP_KERNEL);
436 if (id < 0) {
437 err = id;
438 goto exit;
439 }
440 }
441
442 if (id >= PSE_MAX_PORTS) {
443 dev_warn(&pse_port->dev, "Max number of PSE ports (%d) have been registered", PSE_MAX_PORTS);
444 err = -ENOMEM;
445 goto exit_ida;
446 }
447
448 pse_port->id = id;
449 pse_port->enabled = 0;
450 pse_port->power_allocation = 0;
451 pse_port->priority = pse_data->port_priorities[id];
452 pse_port->last_class_result = CLASS_INVALID;
453 pse_port->last_detect_result = DETECT_NO_DEVICE;
454 pse_port->dev.class = g_pse_class;
455 pse_port->dev.release = pse_port_device_release;
456 dev_set_name(&pse_port->dev, "port%d", id);
457 pse_port->wq = alloc_ordered_workqueue("pse-port%d-wq", 0, pse_port->id);
458 mutex_init(&pse_port->lock);
459
460 INIT_WORK(&pse_port->request_power, pse_port_power_request_handler);
461 INIT_WORK(&pse_port->disconnect, pse_port_power_disconnect_handler);
462
463 err = device_register(&pse_port->dev);
464 if (err) {
465 put_device(&pse_port->dev);
466 goto unlock_exit_ida;
467 }
468
469 mutex_lock(&pse_data->pse_lock);
470 relink_priority_list(&pse_data->cls);
471 mutex_unlock(&pse_data->pse_lock);
472
473 redistribute_power(&pse_data->cls);
474
475 dev_info(&pse_port->dev, "pse core: registered port %d as %s with priority %d\n", pse_port->id, dev_name(&pse_port->dev), pse_port->priority);
476 return pse_port;
477
478
479unlock_exit_ida:
480 mutex_unlock(&pse_data->pse_lock);
481exit_ida:
482 ida_simple_remove(&pse_port_ida, id);
483exit:
484 dev_err(&pse_port->dev, "pse core: unable to register port %d, err = %d\n", id, err);
485 return ERR_PTR(err);
486}
487EXPORT_SYMBOL(pse_port_device_register);
488
489/* pse_port_device_unregister - unregister a pse_port_device
490 *
491 * @port: the pse port class device to destroy
492 */
493void pse_port_device_unregister(struct pse_port_device *port)
494{
495 int id;
496
497 if (get_device(&port->dev) != NULL) {
498 id = port->id;
499 dev_info(&port->dev, "Unregistering port%d\n", id);
500 device_unregister(&port->dev);
501 ida_simple_remove(&pse_port_ida, id);
502 put_device(&port->dev);
503 }
504}
505EXPORT_SYMBOL(pse_port_device_unregister);
506
507static int __init pse_init(void)
508{
509 int i = 0;
510 int err = 0;
511
512 struct pse_data *pse_data = kzalloc(sizeof(struct pse_data), GFP_KERNEL);
513
514 if (!pse_data)
515 return -ENOMEM;
516
517 /* Since kzalloc will zero out the member before returning
518 * the pointer, we need to set every potential port's priority
519 * to the lowest possible (highest numerical value)
520 */
521
522 for (i = 0; i < PSE_MAX_PORTS; i++)
523 pse_data->port_priorities[i] = 0xFF;
524
525 mutex_init(&pse_data->pse_lock);
526
527 pse_data->enabled = 0;
528 pse_data->power_used = 0;
529 pse_data->power_budget = 0;
530
531 /* Now we should set the various class things and such */
532 pse_data->cls.name = PSE_CLASS_NAME_STR;
533 pse_data->cls.owner = THIS_MODULE;
534 pse_data->cls.dev_groups = pse_port_dev_attr_groups;
535
536 /* With necessary fields filled out, lets register the pse class */
537 err = class_register(&pse_data->cls);
538 if (err) {
539 pr_info("Failed to register the PSE class");
540 kfree(pse_data);
541 return err;
542 }
543
544 /* We can now save the pse_data pointer */
545 g_pse_class = &pse_data->cls;
546
547 /* Now we should create the sysfs files for enable, power_budget,ect */
548 pse_class_sysfs_init(&pse_data->cls);
549
550
551 return err;
552}
553
554static void __exit pse_exit(void)
555{
556 struct pse_data *pse_data = get_pse_data(g_pse_class);
557
558 pse_class_sysfs_close(&pse_data->cls);
559 mutex_destroy(&pse_data->pse_lock);
560 class_destroy(&pse_data->cls);
561 ida_destroy(&pse_port_ida);
562 kfree(pse_data);
563}
564
565subsys_initcall(pse_init);
566module_exit(pse_exit);
567
568MODULE_AUTHOR("Kyle Swenson <kswenson@cradlepoint.com>");
569MODULE_DESCRIPTION("PoE (PSE) sysfs class");
570MODULE_LICENSE("GPL");
571