blob: fc2b58ac4a1aac5e39447eb441cdea24a8dead00 [file] [log] [blame]
Kyle Swenson7900a3c2021-08-12 14:34:44 -06001/* * ./kernel_modules/pse/pse-sysfs.c
2 *
3 * This file defines the sysfs interface for the PSE class
4 *
5 * Author: Cradlepoint Technology, Inc. <source@cradlepoint.com>
6 * Kyle Swenson <kswenson@cradlepoint.com>
7 *
8 * Copyright 2017 Cradlepoint Technology, Inc.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2
12 * as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 */
19#include <linux/kernel.h>
20#include <linux/module.h>
21#include <linux/device.h>
22#include <linux/err.h>
23#include <linux/of.h>
24#include <linux/slab.h>
25#include <linux/sysfs.h>
26#include <linux/idr.h>
27#include <linux/spinlock.h>
28#include <linux/list_sort.h>
29#include <linux/sort.h>
30
31#include "pse-port.h"
32#include "pse-class.h"
33#include "pse-sysfs.h"
34
35char *pse_detect_strings[] = {
36 "0:DETECT_NO_DEVICE",
37 "1:DETECT_NORMAL_DEVICE", /* Short */
38 "2:DETECT_LEGACY_DEVICE", /* High cap devices */
39 "3:DETECT_POWERED_DEVICE",/* Signature resistance good */ /* Maybe we want to even break this down into detect Type of device iff that's a detect-time thing */
40 "4:DETECT_ERROR",
41 NULL
42};
43EXPORT_SYMBOL(pse_detect_strings);
44char *pse_class_strings[] = {
45 "0:PD_CLASS_UNKNOWN",
46 "1:PD_CLASS_1",
47 "2:PD_CLASS_2",
48 "3:PD_CLASS_3",
49 "4:PD_CLASS_4",
50 "5:PD_CLASS_5",
51 "6:PD_CLASS_6",
52 "7:PD_CLASS_7",
53 "8:PD_CLASS_8",
54 "1D:PD_CLASS_1D",
55 "2D:PD_CLASS_2D",
56 "3D:PD_CLASS_3D",
57 "4D:PD_CLASS_4D",
58 "5D:PD_CLASS_5D",
59 "9:PD_CLASS_INVALID",
60 NULL
61};
62EXPORT_SYMBOL(pse_class_strings);
63
64uint16_t pse_port_current_table[] = {
65 375, /* Class 0 (unknown) */
66 112, /* Class 1 */
67 206, /* Class 2 */
68 375, /* Class 3 */
69 638, /* Class 4 (Type 2) */
70 938, /* Class 5 */
71 1250, /* Class 6 */
72 1563, /* Class 7 */
73 1875, /* Class 8 */
74 2*112, /* Class 1D */
75 2*206, /* Class 2D */
76 2*375, /* Class 3D */
77 2*638, /* Class 4D */
78 2*938 /* Class 5D */
79};
80
81uint32_t pse_port_power_table[] = {
82 15400, /* Class 0 (unknown) */
83 4000, /* Class 1 */
84 7000, /* Class 2 */
85 15400, /* Class 3 */
86 30000, /* Class 4 */
87 45000, /* Class 5 */
88 60000, /* Class 6 */
89 75000, /* Class 7 */
90 90000, /* Class 8 */
91 2*4000, /* Class 1D */
92 2*7000, /* Class 2D */
93 2*15400, /* Class 3D */
94 2*30000, /* Class 4D */
95 2*45000, /* Class 5D */
96};
Kyle Swensond6f4dd02022-04-16 22:55:40 -060097EXPORT_SYMBOL(pse_port_power_table);
Kyle Swenson7900a3c2021-08-12 14:34:44 -060098/* ============ PSE CLASS ATTRIBUTES =================
99* PSE Class level attributes; these appear under /sys/class/pse/
100*/
101static ssize_t
102pse_version_show(struct class *class, struct class_attribute *attr, char *buf)
103{
104 return sprintf(buf, "%s\n", PSE_VERSION_STR);
105}
106
107static struct class_attribute pse_version = __ATTR_RO(pse_version);
108
109
110
111/* -1 for false
112 * 0 for no idea
113 * +1 for true
114 */
115static int is_trueish(const char *buf, size_t len)
116{
117 if (len == 0)
118 return 0;
119
120 switch (buf[0]) {
121 case '1':
122 /* ASCII char 0x31 => '1' means true*/
123 case 'y':
124 case 'Y':
125 /* [yY].* */
126 case 't':
127 case 'T':
128 /* [tT].* */
129 case 'e':
130 case 'E':
131 /* [eE] for enable */
132 return 1;
133 case '0':
134 /* ASCII char 0x30 => '0' means false */
135 case 'n':
136 case 'N':
137 /* [nN].* for No */
138 case 'F':
139 case 'f':
140 /* [fF].* for false */
141 case 'd':
142 case 'D':
143 /* [dD] for disable*/
144 return -1;
145 case 'o':
146 case 'O':
147 /* Could be On, or Off need to look more */
148 break;
149 default:
150 /* Bail if we don't recognize the first char */
151 return 0;
152 }
153 /*If we're here we know the first char is [oO] */
154 /* But if we've only got one char in the buffer, we can't determine so bail*/
155 if (len == 1)
156 return 0;
157
158 /* Here, we know len > 1, so at least two */
159 switch (buf[1]) {
160 case 'n':
161 case 'N':
162 /* for [oO][nN].* */
163 return 1;
164 case 'f':
165 case 'F':
166 /* for [oO][fF].* */
167 return -1;
168 default:
169 return 0;
170 }
171 return 0;
172}
173
174static ssize_t
175port_priority_show(struct class *class, struct class_attribute *attr, char *buf)
176{
177 struct class_dev_iter iter;
178 struct device *dev;
179 int bput = 0;
180
181 class_dev_iter_init(&iter, class, NULL, NULL);
182 dev = class_dev_iter_next(&iter);
183 while (dev) {
184 bput += snprintf((char *)(buf+bput), 32, "port%d -> priority %d\n", to_pse_port_device(dev)->id, to_pse_port_device(dev)->priority);
185 dev = class_dev_iter_next(&iter);
186 }
187 class_dev_iter_exit(&iter);
188 return bput;
189}
190
191/* We are implying port and specifying priority:
192 * |Port 0 relative priority 2
193 * | |Port 1 is priority 3
194 * | | |Port 2 has priority 1
195 * | | | |Port 3 is priority 0 (highest)
196 * 2 3 1 0
197 *
198*/
199static ssize_t
200port_priority_store(struct class *class, struct class_attribute *attr, const char *buff, size_t count)
201{
202 struct device *dev;
203 struct class_dev_iter iter;
204 char *substr;
205 char *lbuffs;
206
207 int vi = 0;
208 size_t bget = 0;
209 uint8_t port_priority = 0;
210 struct pse_data *pse_data = get_pse_data(class);
211 struct pse_port_device *port;
212 char *local_buff = kcalloc(count, sizeof(char), GFP_KERNEL);
213
214 lbuffs = memcpy((char *) local_buff, (const char *) buff, count);
215
216 mutex_lock(&pse_data->pse_lock);
217 while (bget < count && vi < PSE_MAX_PORTS) {
218 substr = strsep(&lbuffs, " ");
219 if (substr == NULL) {
220 break;
221 }
222 printk(KERN_INFO "Looking at substr = %s\n", substr);
223 if (kstrtou8(substr, 0, &port_priority) == 0) {
224 pse_data->port_priorities[vi++] = port_priority;
225 bget += strlen(substr);
226 } else {
227 printk(KERN_WARNING "Unable to parse the number out of %s\n", substr);
228 }
229 }
230 class_dev_iter_init(&iter, class, NULL, NULL);
231 dev = class_dev_iter_next(&iter);
232 while (dev) {
233 port = to_pse_port_device(dev);
234 port->priority = pse_data->port_priorities[port->id];
235 dev_dbg(dev, "Storing port_priority %hhu to port%u", port->priority, port->id);
236 dev = class_dev_iter_next(&iter);
237 }
238 class_dev_iter_exit(&iter);
239 relink_priority_list(class); /* Rebuild the dubly linked list */
240 redistribute_power(class); /* Force every port off & on again, to get them to repower in correct, priority order */
241 mutex_unlock(&pse_data->pse_lock);
242
243 kfree(local_buff);
244 return count;
245}
246static struct class_attribute port_priority = __ATTR_RW(port_priority);
247
248static ssize_t
249power_budget_show(struct class *class, struct class_attribute *attr, char *buf)
250{
251 struct pse_data *pse_data = get_pse_data(class);
252
253 return sprintf(buf, "%d", pse_data->power_budget);
254}
255static ssize_t
256power_budget_store(struct class *class, struct class_attribute *attr, const char *buff, size_t count)
257{
258 int new;
259 int res;
260 struct pse_data *pse_data = get_pse_data(class);
261
262 res = kstrtoint(buff, 0, &new);
263 if (res != 0)
264 return res;
265 mutex_lock(&pse_data->pse_lock);
266 pse_data->power_budget = new;
267 redistribute_power(&pse_data->cls);
268 mutex_unlock(&pse_data->pse_lock);
269 return count;
270}
271static struct class_attribute power_budget = __ATTR_RW(power_budget);
272
273static ssize_t
274enable_show(struct class *class, struct class_attribute *attr, char *buf)
275{
276 struct pse_data *pse_data = get_pse_data(class);
277
278 return sprintf(buf, pse_data->enabled ? "True" : "False");
279}
280
281static ssize_t
282enable_store(struct class *class, struct class_attribute *attr, const char *buf, size_t count)
283{
284
285 struct pse_data *pse_data = get_pse_data(class);
286 int t = is_trueish(buf, count);
287
288 if (t == 0)
289 return -EINVAL;
290
291 mutex_lock(&pse_data->pse_lock);
292 if (t > 0)
293 pse_data->enabled = 1;
294 else
295 pse_data->enabled = 0;
296
297 redistribute_power(class);
298 mutex_unlock(&pse_data->pse_lock);
299 return count;
300}
301static struct class_attribute enable = __ATTR_RW(enable);
302
303static ssize_t
304power_used_show(struct class *class, struct class_attribute *attr, char *buf)
305{
306 struct pse_data *pse_data = get_pse_data(class);
307
308 return sprintf(buf, "%d", pse_data->power_used);
309}
310static struct class_attribute power_used = __ATTR_RO(power_used);
311
312int pse_class_sysfs_init(struct class *class)
313{
314 int err;
315
316 err = class_create_file(class, &pse_version);
317 if (err) {
318 pr_err("couldn't register pse class version file\n");
319 return err;
320 }
321 err = class_create_file(class, &port_priority);
322 if (err) {
323 pr_err("couldn't register pse class port priority file\n");
324 return err;
325 }
326 err = class_create_file(class, &power_budget);
327 if (err) {
328 pr_err("couldn't register pse class port priority file\n");
329 return err;
330 }
331 err = class_create_file(class, &enable);
332 if (err) {
333 pr_err("couldn't register pse class enable file\n");
334 return err;
335 }
336 err = class_create_file(class, &power_used);
337 if (err) {
338 pr_err("couldn't register pse class enable file\n");
339 return err;
340 }
341 return 0;
342}
343void pse_class_sysfs_close(struct class *class)
344{
345 class_remove_file(class, &pse_version);
346 class_remove_file(class, &port_priority);
347 class_remove_file(class, &power_budget);
348 class_remove_file(class, &enable);
349 class_remove_file(class, &power_used);
350}
351
352
353/* ============= PSE PORT DEVICE ATTRIBUTES ==================
354 * These are attributes that appear at /sys/class/pse/portX/<attribute>
355 * These Attributes DO NOT EXIST until a device driver has registered a port device with the pse class!
356 */
357
358/* current_show : Display the current in mA being sourced by the port
359*/
360static ssize_t
361pd_current_show(struct device *dev, struct device_attribute *attr, char *buf)
362{
363 int pd_current = 0;
364 struct pse_port_device *port = to_pse_port_device(dev);
365
366 dev_dbg(dev, "%s:%d\n", __func__, __LINE__);
367 if (!port || !port->ops->pd_current || !port->ops->pd_powered)
368 return -EINVAL;
369
370 if (port->enabled && port->ops->pd_powered(dev))
371 pd_current = to_pse_port_device(dev)->ops->pd_current(dev);
372
373 return sprintf(buf, "%d\n", pd_current);
374}
375
Kyle Swensond6f4dd02022-04-16 22:55:40 -0600376static struct device_attribute port_current = __ATTR_RO(pd_current);
Kyle Swenson7900a3c2021-08-12 14:34:44 -0600377
378/* voltage_show: Show the port's voltage
379 */
380static ssize_t
381port_enable_show(struct device *dev, struct device_attribute *attr, char *buf)
382{
383 return sprintf(buf,
384 to_pse_port_device(dev)->enabled ? "True" : "False");
385}
386
387static ssize_t
388port_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
389{
390 int t = is_trueish(buf, count);
391 struct pse_port_device *port = to_pse_port_device(dev);
392
393 if (t == 0)
394 return -EINVAL;
395
396 mutex_lock(&port->lock);
397
398 if (t < 0) {
399 port->enabled = 0;
400 queue_work(port->wq, &port->disconnect);
401 } else {
402 port->enabled = 1;
403 }
404 mutex_unlock(&port->lock);
405
406 return count;
407}
408static struct device_attribute port_enable = __ATTR_RW(port_enable);
409/* voltage_show: Show the port's voltage
410 */
411static ssize_t
412pd_voltage_show(struct device *dev, struct device_attribute *attr, char *buf)
413{
414 int pd_voltage = 0;
415 struct pse_port_device *port = to_pse_port_device(dev);
416
417 if (!port || !port->ops->pd_voltage || !port->ops->pd_powered)
418 return -EINVAL;
419
420 if (port->enabled && port->ops->pd_powered(dev)) {
421 pd_voltage = port->ops->pd_voltage(dev);
422 }
423 return sprintf(buf, "%d\n", pd_voltage);
424}
425
426static struct device_attribute port_voltage = __ATTR_RO(pd_voltage);
427
428/* class_show : Show the port's last classification result
429*/
430static ssize_t
431class_show(struct device *dev, struct device_attribute *attr, char *buf)
432{
433 uint8_t lc;
434 struct pse_port_device *p = to_pse_port_device(dev);
435
436 if (!p->enabled) {
437 return sprintf(buf, "%s", pse_class_strings[CLASS_INVALID]);
438 }
439 if (p->ops->update_classification) {
440 p->ops->update_classification(dev);
441 } else {
442 mutex_lock(&p->lock);
443 p->last_class_result = p->ops->last_classification(dev);
444 mutex_unlock(&p->lock);
445 }
446
447 mutex_lock(&p->lock);
448 lc = p->last_class_result;
449 mutex_unlock(&p->lock);
450
451 if (lc > CLASS_INVALID) {
452 dev_warn(dev, "PSE Class saw invalid class code 0x%02X\n", lc);
453 lc = CLASS_INVALID;
454 }
455 return sprintf(buf, "%s", pse_class_strings[lc]);
456}
457
458static ssize_t
459class_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
460{
461 int t;
462 struct pse_port_device *port = to_pse_port_device(dev);
463
464 if (!port || !port->ops->enable_classify)
465 return -EINVAL;
466
467 t = is_trueish(buf, count);
468 if (t == 0)
469 return -EINVAL;
470
471 if (t < 0) {
472 dev_dbg(dev, "disabling classification for port %d\n", to_pse_port_device(dev)->port_id);
473 port->ops->enable_classify(dev, 0);
474 } else {
475 dev_dbg(dev, "enabling classification for port %d\n", to_pse_port_device(dev)->port_id);
476 port->ops->enable_classify(dev, 1);
477 }
478 return count;
479}
480static struct device_attribute port_class = __ATTR_RW(class);
481
482/* detect_show : show the ports' last detection_result
483*/
484static ssize_t
485detect_show(struct device *dev, struct device_attribute *attr, char *buf)
486{
487
488 uint8_t detval;
489 struct pse_port_device *p = to_pse_port_device(dev);
490
491 if (!p->enabled) {
492 return sprintf(buf, "%s", pse_detect_strings[DETECT_NO_DEVICE]);
493 }
494 if (p->ops->update_detection) {
495 p->ops->update_detection(dev);
496 } else {
497 mutex_lock(&p->lock);
498 p->last_detect_result = p->ops->last_detection(dev);
499 mutex_unlock(&p->lock);
500 }
501 mutex_lock(&p->lock);
502 detval = p->last_detect_result;
503 mutex_unlock(&p->lock);
504
505 if (detval >= PSE_NUM_DETECT_EVENTS) {
506 dev_warn(dev, "PSE class saw an invalid detection code 0x%02x\n", detval);
507 detval = DETECT_ERROR;
508 }
509 return sprintf(buf, "%s", pse_detect_strings[detval]);
510}
511static ssize_t
512detect_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
513{
514 int enable;
515 struct pse_port_device *port = to_pse_port_device(dev);
516
517 if (!port || !port->ops->enable_detect)
518 return -EINVAL;
519
520 enable = is_trueish(buf, count);
521 if (enable == 0)
522 return -EINVAL;
523
524 if (enable < 0) {
525 port->ops->enable_detect(dev, 0);
526 dev_dbg(dev, "disabling detection for port %d\n", to_pse_port_device(dev)->port_id);
527 } else {
528 port->ops->enable_detect(dev, 1);
529 dev_dbg(dev, "enabling detection for port %d\n", to_pse_port_device(dev)->port_id);
530 }
531 return count;
532}
533static struct device_attribute port_detect = __ATTR_RW(detect);
534
535/*port_power_allocation_show: Show the allocation for this port
536*/
537static ssize_t
538port_power_allocation_show(struct device *dev, struct device_attribute *attr, char *buf)
539{
540 struct pse_port_device *port = to_pse_port_device(dev);
541
542 if (!port)
543 return -EINVAL;
544
545 return sprintf(buf, "%d", port->power_allocation);
546}
Kyle Swensond6f4dd02022-04-16 22:55:40 -0600547
548/*port_power_allocation_store: Request a power allocation change for this port
549*/
550static ssize_t
551port_power_allocation_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
552{
553 uint32_t power_request;
554 struct pse_port_device *port = to_pse_port_device(dev);
555 if (!port)
556 return -EINVAL;
557
558 if (kstrtou32(buf, 0, &power_request) != 0)
559 return -EINVAL;
560
561 dev_info(dev, "Got %u mW for power request\n", power_request);
562 mutex_lock(&port->lock);
563 port->power_request = power_request;
564 queue_work(port->wq, &port->request_power);
565 mutex_unlock(&port->lock);
566
567 return count;
568}
569static struct device_attribute port_power_allocation = __ATTR_RW(port_power_allocation);
570
Kyle Swenson7900a3c2021-08-12 14:34:44 -0600571
572/* port_power_store : power "on/off" a port */
573static ssize_t
574port_power_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
575{
576 int t;
577
578 dev_dbg(dev, "%s:%d\n", __func__, __LINE__);
579 if (!to_pse_port_device(dev)->ops->apply_power)
580 return -EINVAL;
581
582 t = is_trueish(buf, count);
583 if (t == 0)
584 return -EINVAL;
585
586 if (t > 0)
587 to_pse_port_device(dev)->ops->apply_power(dev, 1);
588 else
589 to_pse_port_device(dev)->ops->apply_power(dev, 0);
590
591 return count;
592
593}
594static ssize_t
595port_power_show(struct device *dev, struct device_attribute *attr, char *buf)
596{
597 if (!to_pse_port_device(dev)->ops->pd_powered)
598 return -EINVAL;
599
600 return sprintf(buf, to_pse_port_device(dev)->ops->pd_powered(dev) ? "True" : "False");
601}
602static struct device_attribute port_power = __ATTR_RW(port_power);
603
604
605static ssize_t
606config_show(struct device *dev, struct device_attribute *attr, char *buf)
607{
608 struct pse_port_config config;
609
610 if (!to_pse_port_device(dev)->ops->get_config)
611 return -EINVAL;
612
613 to_pse_port_device(dev)->ops->get_config(dev, &config);
614
615 return sprintf(buf, "enabled:%s\nmanaged:%s\ntype:%d\n"
616 "disconnect_detection:%s\npd_detection_enabled:%s\n"
617 "pd_classification_enabled:%s\n"
618 "legacy_device_enable:%s\ncurrent_limit:%d\n",
619 (config.enabled ? "True" : "False"),
620 (config.managed ? "True" : "False"),
621 (config.port_type+1),
622 (config.disconnect_event ? "True" : "False"),
623 (config.detect_enable ? "True" : "False"),
624 (config.classify_enable ? "True" : "False"),
625 (config.legacy_enable ? "True" : "False"),
626 config.current_limit);
627
628}
629static struct device_attribute port_config = __ATTR_RO(config);
630
631static ssize_t
632reset_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
633{
634 if (!to_pse_port_device(dev)->ops->reset)
635 return -EINVAL;
636
637 to_pse_port_device(dev)->ops->reset(dev);
638 return count;
639}
640static struct device_attribute port_reset = __ATTR_WO(reset);
641
642struct attribute *dev_attrs[] = {
643 &port_current.attr,
644 &port_voltage.attr,
645 &port_class.attr,
646 &port_detect.attr,
647 &port_power.attr,
648 &port_power_allocation.attr,
649 &port_enable.attr,
650 &port_config.attr,
651 &port_reset.attr,
652 NULL,
653};
654
655struct attribute_group dev_attr_group = {
656 .attrs = dev_attrs,
657
658};
659
660const struct attribute_group *pse_port_dev_attr_groups[] = {
661 &dev_attr_group,
662 NULL,
663};