xref: /linux/drivers/platform/chrome/cros_ec_lightbar.c (revision fab183d632628381b466a41479489541ac0e29a0)
1 // SPDX-License-Identifier: GPL-2.0+
2 // Expose the Chromebook Pixel lightbar to userspace
3 //
4 // Copyright (C) 2014 Google, Inc.
5 
6 #include <linux/ctype.h>
7 #include <linux/delay.h>
8 #include <linux/device.h>
9 #include <linux/fs.h>
10 #include <linux/kobject.h>
11 #include <linux/kstrtox.h>
12 #include <linux/module.h>
13 #include <linux/platform_data/cros_ec_commands.h>
14 #include <linux/platform_data/cros_ec_proto.h>
15 #include <linux/platform_device.h>
16 #include <linux/sched.h>
17 #include <linux/types.h>
18 #include <linux/uaccess.h>
19 #include <linux/slab.h>
20 
21 #define DRV_NAME "cros-ec-lightbar"
22 
23 /* Rate-limit the lightbar interface to prevent DoS. */
24 static unsigned long lb_interval_jiffies = 50 * HZ / 1000;
25 
26 /*
27  * Whether or not we have given userspace control of the lightbar.
28  * If this is true, we won't do anything during suspend/resume.
29  */
30 static bool userspace_control;
31 
32 /*
33  * Whether or not the lightbar supports the manual suspend commands.
34  * The Pixel 2013 (Link) does not while all other devices with a
35  * lightbar do.
36  */
37 static bool has_manual_suspend;
38 
39 /*
40  * Lightbar version
41  */
42 static int lb_version;
43 
interval_msec_show(struct device * dev,struct device_attribute * attr,char * buf)44 static ssize_t interval_msec_show(struct device *dev,
45 				  struct device_attribute *attr, char *buf)
46 {
47 	unsigned long msec = lb_interval_jiffies * 1000 / HZ;
48 
49 	return sysfs_emit(buf, "%lu\n", msec);
50 }
51 
interval_msec_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)52 static ssize_t interval_msec_store(struct device *dev,
53 				   struct device_attribute *attr,
54 				   const char *buf, size_t count)
55 {
56 	unsigned long msec;
57 
58 	if (kstrtoul(buf, 0, &msec))
59 		return -EINVAL;
60 
61 	lb_interval_jiffies = msec * HZ / 1000;
62 
63 	return count;
64 }
65 
66 static DEFINE_MUTEX(lb_mutex);
67 /* Return 0 if able to throttle correctly, error otherwise */
lb_throttle(void)68 static int lb_throttle(void)
69 {
70 	static unsigned long last_access;
71 	unsigned long now, next_timeslot;
72 	long delay;
73 	int ret = 0;
74 
75 	mutex_lock(&lb_mutex);
76 
77 	now = jiffies;
78 	next_timeslot = last_access + lb_interval_jiffies;
79 
80 	if (time_before(now, next_timeslot)) {
81 		delay = (long)(next_timeslot) - (long)now;
82 		set_current_state(TASK_INTERRUPTIBLE);
83 		if (schedule_timeout(delay) > 0) {
84 			/* interrupted - just abort */
85 			ret = -EINTR;
86 			goto out;
87 		}
88 		now = jiffies;
89 	}
90 
91 	last_access = now;
92 out:
93 	mutex_unlock(&lb_mutex);
94 
95 	return ret;
96 }
97 
alloc_lightbar_cmd_msg(struct cros_ec_dev * ec)98 static struct cros_ec_command *alloc_lightbar_cmd_msg(struct cros_ec_dev *ec)
99 {
100 	int len = max(ec->ec_dev->max_response, ec->ec_dev->max_request);
101 	struct cros_ec_command *msg;
102 
103 	msg = kmalloc(sizeof(*msg) + len, GFP_KERNEL);
104 	if (!msg)
105 		return NULL;
106 
107 	msg->version = 0;
108 	msg->command = EC_CMD_LIGHTBAR_CMD + ec->cmd_offset;
109 	/*
110 	 * Default sizes for regular commands.
111 	 * Can be set smaller to optimize transfer,
112 	 * larger when sending large light sequences.
113 	 */
114 	msg->outsize = sizeof(struct ec_params_lightbar);
115 	msg->insize = sizeof(struct ec_response_lightbar);
116 
117 	return msg;
118 }
119 
get_lightbar_version(struct cros_ec_dev * ec,uint32_t * ver_ptr,uint32_t * flg_ptr)120 static int get_lightbar_version(struct cros_ec_dev *ec,
121 				uint32_t *ver_ptr, uint32_t *flg_ptr)
122 {
123 	struct ec_params_lightbar *param;
124 	struct ec_response_lightbar *resp;
125 	struct cros_ec_command *msg;
126 	int ret;
127 
128 	msg = alloc_lightbar_cmd_msg(ec);
129 	if (!msg)
130 		return 0;
131 
132 	param = (struct ec_params_lightbar *)msg->data;
133 	param->cmd = LIGHTBAR_CMD_VERSION;
134 	msg->outsize = sizeof(param->cmd);
135 	msg->insize = sizeof(resp->version);
136 	ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
137 	if (ret < 0 && ret != -EINVAL) {
138 		ret = 0;
139 		goto exit;
140 	}
141 
142 	switch (msg->result) {
143 	case EC_RES_INVALID_PARAM:
144 		/* Pixel had no version command. */
145 		if (ver_ptr)
146 			*ver_ptr = 0;
147 		if (flg_ptr)
148 			*flg_ptr = 0;
149 		ret = 1;
150 		goto exit;
151 
152 	case EC_RES_SUCCESS:
153 		resp = (struct ec_response_lightbar *)msg->data;
154 
155 		/* Future devices w/lightbars should implement this command */
156 		if (ver_ptr)
157 			*ver_ptr = resp->version.num;
158 		if (flg_ptr)
159 			*flg_ptr = resp->version.flags;
160 		ret = 1;
161 		goto exit;
162 	}
163 
164 	/* Anything else (ie, EC_RES_INVALID_COMMAND) - no lightbar */
165 	ret = 0;
166 exit:
167 	kfree(msg);
168 	return ret;
169 }
170 
version_show(struct device * dev,struct device_attribute * attr,char * buf)171 static ssize_t version_show(struct device *dev,
172 			    struct device_attribute *attr, char *buf)
173 {
174 	uint32_t version = 0, flags = 0;
175 	struct cros_ec_dev *ec = to_cros_ec_dev(dev);
176 	int ret;
177 
178 	ret = lb_throttle();
179 	if (ret)
180 		return ret;
181 
182 	/* This should always succeed, because we check during init. */
183 	if (!get_lightbar_version(ec, &version, &flags))
184 		return -EIO;
185 
186 	return sysfs_emit(buf, "%d %d\n", version, flags);
187 }
188 
num_segments_show(struct device * dev,struct device_attribute * attr,char * buf)189 static ssize_t num_segments_show(struct device *dev,
190 				 struct device_attribute *attr, char *buf)
191 {
192 	struct ec_params_lightbar *param;
193 	struct ec_response_lightbar *resp;
194 	struct cros_ec_command *msg;
195 	struct cros_ec_dev *ec = to_cros_ec_dev(dev);
196 	uint32_t num = 0;
197 	int ret;
198 
199 	ret = lb_throttle();
200 	if (ret)
201 		return ret;
202 
203 	msg = alloc_lightbar_cmd_msg(ec);
204 	if (!msg)
205 		return -ENOMEM;
206 
207 	param = (struct ec_params_lightbar *)msg->data;
208 	param->cmd = LIGHTBAR_CMD_GET_PARAMS_V3;
209 	msg->outsize = sizeof(param->cmd);
210 	msg->insize = sizeof(resp->get_params_v3);
211 	ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
212 	if (ret < 0 && ret != -EINVAL)
213 		goto exit;
214 
215 	if (msg->result == EC_RES_SUCCESS) {
216 		resp = (struct ec_response_lightbar *)msg->data;
217 		num = resp->get_params_v3.reported_led_num;
218 	}
219 
220 	/*
221 	 * Anything else (ie, EC_RES_INVALID_COMMAND) - no direct control over
222 	 * LEDs, return that no leds are supported.
223 	 */
224 	ret = sysfs_emit(buf, "%u\n", num);
225 exit:
226 	kfree(msg);
227 	return ret;
228 }
229 
brightness_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)230 static ssize_t brightness_store(struct device *dev,
231 				struct device_attribute *attr,
232 				const char *buf, size_t count)
233 {
234 	struct ec_params_lightbar *param;
235 	struct cros_ec_command *msg;
236 	int ret;
237 	unsigned int val;
238 	struct cros_ec_dev *ec = to_cros_ec_dev(dev);
239 
240 	if (kstrtouint(buf, 0, &val))
241 		return -EINVAL;
242 
243 	msg = alloc_lightbar_cmd_msg(ec);
244 	if (!msg)
245 		return -ENOMEM;
246 
247 	param = (struct ec_params_lightbar *)msg->data;
248 	param->cmd = LIGHTBAR_CMD_SET_BRIGHTNESS;
249 	param->set_brightness.num = val;
250 	ret = lb_throttle();
251 	if (ret)
252 		goto exit;
253 
254 	ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
255 	if (ret < 0)
256 		goto exit;
257 
258 	ret = count;
259 exit:
260 	kfree(msg);
261 	return ret;
262 }
263 
264 
265 /*
266  * We expect numbers, and we'll keep reading until we find them, skipping over
267  * any whitespace (sysfs guarantees that the input is null-terminated). Every
268  * four numbers are sent to the lightbar as <LED,R,G,B>. We fail at the first
269  * parsing error, if we don't parse any numbers, or if we have numbers left
270  * over.
271  */
led_rgb_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)272 static ssize_t led_rgb_store(struct device *dev, struct device_attribute *attr,
273 			     const char *buf, size_t count)
274 {
275 	struct ec_params_lightbar *param;
276 	struct cros_ec_command *msg;
277 	struct cros_ec_dev *ec = to_cros_ec_dev(dev);
278 	unsigned int val[4];
279 	int ret, i = 0, j = 0, ok = 0;
280 
281 	msg = alloc_lightbar_cmd_msg(ec);
282 	if (!msg)
283 		return -ENOMEM;
284 
285 	do {
286 		/* Skip any whitespace */
287 		while (*buf && isspace(*buf))
288 			buf++;
289 
290 		if (!*buf)
291 			break;
292 
293 		ret = sscanf(buf, "%i", &val[i++]);
294 		if (ret == 0)
295 			goto exit;
296 
297 		if (i == 4) {
298 			param = (struct ec_params_lightbar *)msg->data;
299 			param->cmd = LIGHTBAR_CMD_SET_RGB;
300 			param->set_rgb.led = val[0];
301 			param->set_rgb.red = val[1];
302 			param->set_rgb.green = val[2];
303 			param->set_rgb.blue = val[3];
304 			/*
305 			 * Throttle only the first of every four transactions,
306 			 * so that the user can update all four LEDs at once.
307 			 */
308 			if ((j++ % 4) == 0) {
309 				ret = lb_throttle();
310 				if (ret)
311 					goto exit;
312 			}
313 
314 			ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
315 			if (ret < 0)
316 				goto exit;
317 
318 			i = 0;
319 			ok = 1;
320 		}
321 
322 		/* Skip over the number we just read */
323 		while (*buf && !isspace(*buf))
324 			buf++;
325 
326 	} while (*buf);
327 
328 exit:
329 	kfree(msg);
330 	return (ok && i == 0) ? count : -EINVAL;
331 }
332 
333 static char const *seqname[] = {
334 	"ERROR", "S5", "S3", "S0", "S5S3", "S3S0",
335 	"S0S3", "S3S5", "STOP", "RUN", "KONAMI",
336 	"TAP", "PROGRAM",
337 };
338 
sequence_show(struct device * dev,struct device_attribute * attr,char * buf)339 static ssize_t sequence_show(struct device *dev,
340 			     struct device_attribute *attr, char *buf)
341 {
342 	struct ec_params_lightbar *param;
343 	struct ec_response_lightbar *resp;
344 	struct cros_ec_command *msg;
345 	int ret;
346 	struct cros_ec_dev *ec = to_cros_ec_dev(dev);
347 
348 	msg = alloc_lightbar_cmd_msg(ec);
349 	if (!msg)
350 		return -ENOMEM;
351 
352 	param = (struct ec_params_lightbar *)msg->data;
353 	param->cmd = LIGHTBAR_CMD_GET_SEQ;
354 	ret = lb_throttle();
355 	if (ret)
356 		goto exit;
357 
358 	ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
359 	if (ret < 0) {
360 		ret = sysfs_emit(buf, "XFER / EC ERROR %d / %d\n", ret, msg->result);
361 		goto exit;
362 	}
363 
364 	resp = (struct ec_response_lightbar *)msg->data;
365 	if (resp->get_seq.num >= ARRAY_SIZE(seqname))
366 		ret = sysfs_emit(buf, "%d\n", resp->get_seq.num);
367 	else
368 		ret = sysfs_emit(buf, "%s\n", seqname[resp->get_seq.num]);
369 
370 exit:
371 	kfree(msg);
372 	return ret;
373 }
374 
lb_send_empty_cmd(struct cros_ec_dev * ec,uint8_t cmd)375 static int lb_send_empty_cmd(struct cros_ec_dev *ec, uint8_t cmd)
376 {
377 	struct ec_params_lightbar *param;
378 	struct cros_ec_command *msg;
379 	int ret;
380 
381 	msg = alloc_lightbar_cmd_msg(ec);
382 	if (!msg)
383 		return -ENOMEM;
384 
385 	param = (struct ec_params_lightbar *)msg->data;
386 	param->cmd = cmd;
387 
388 	ret = lb_throttle();
389 	if (ret)
390 		goto error;
391 
392 	ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
393 	if (ret < 0)
394 		goto error;
395 
396 	ret = 0;
397 error:
398 	kfree(msg);
399 
400 	return ret;
401 }
402 
lb_manual_suspend_ctrl(struct cros_ec_dev * ec,uint8_t enable)403 static int lb_manual_suspend_ctrl(struct cros_ec_dev *ec, uint8_t enable)
404 {
405 	struct ec_params_lightbar *param;
406 	struct cros_ec_command *msg;
407 	int ret;
408 
409 	msg = alloc_lightbar_cmd_msg(ec);
410 	if (!msg)
411 		return -ENOMEM;
412 
413 	param = (struct ec_params_lightbar *)msg->data;
414 
415 	param->cmd = LIGHTBAR_CMD_MANUAL_SUSPEND_CTRL;
416 	param->manual_suspend_ctrl.enable = enable;
417 
418 	ret = lb_throttle();
419 	if (ret)
420 		goto error;
421 
422 	ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
423 	if (ret < 0)
424 		goto error;
425 
426 	ret = 0;
427 error:
428 	kfree(msg);
429 
430 	return ret;
431 }
432 
sequence_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)433 static ssize_t sequence_store(struct device *dev, struct device_attribute *attr,
434 			      const char *buf, size_t count)
435 {
436 	struct ec_params_lightbar *param;
437 	struct cros_ec_command *msg;
438 	unsigned int num;
439 	int ret, len;
440 	struct cros_ec_dev *ec = to_cros_ec_dev(dev);
441 
442 	for (len = 0; len < count; len++)
443 		if (!isalnum(buf[len]))
444 			break;
445 
446 	for (num = 0; num < ARRAY_SIZE(seqname); num++)
447 		if (!strncasecmp(seqname[num], buf, len))
448 			break;
449 
450 	if (num >= ARRAY_SIZE(seqname)) {
451 		ret = kstrtouint(buf, 0, &num);
452 		if (ret)
453 			return ret;
454 	}
455 
456 	msg = alloc_lightbar_cmd_msg(ec);
457 	if (!msg)
458 		return -ENOMEM;
459 
460 	param = (struct ec_params_lightbar *)msg->data;
461 	param->cmd = LIGHTBAR_CMD_SEQ;
462 	param->seq.num = num;
463 	msg->outsize = offsetof(typeof(*param), seq) + sizeof(param->seq);
464 	msg->insize = 0;
465 	ret = lb_throttle();
466 	if (ret)
467 		goto exit;
468 
469 	ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
470 	if (ret < 0)
471 		goto exit;
472 
473 	ret = count;
474 exit:
475 	kfree(msg);
476 	return ret;
477 }
478 
program_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)479 static ssize_t program_store(struct device *dev, struct device_attribute *attr,
480 			     const char *buf, size_t count)
481 {
482 	size_t extra_bytes, max_size;
483 	struct ec_params_lightbar *param;
484 	struct cros_ec_command *msg;
485 	struct cros_ec_dev *ec = to_cros_ec_dev(dev);
486 	int ret;
487 
488 	/*
489 	 * We might need to reject the program for size reasons. The EC
490 	 * enforces a maximum program size, but we also don't want to try
491 	 * and send a program that is too big for the protocol. In order
492 	 * to ensure the latter, we also need to ensure we have extra bytes
493 	 * to represent the rest of the packet.
494 	 * With V3, larger program can be sent, limited only by the EC.
495 	 * Only the protocol limit the payload size.
496 	 */
497 	if (lb_version < 3) {
498 		extra_bytes = sizeof(*param) - sizeof(param->set_program.data);
499 		max_size = min(EC_LB_PROG_LEN, ec->ec_dev->max_request - extra_bytes);
500 		if (count > max_size) {
501 			dev_err(dev, "Program is %zu bytes, too long to send (max: %zu)",
502 				count, max_size);
503 
504 			return -EINVAL;
505 		}
506 	} else {
507 		/*
508 		 * Bound the payload strictly by the maximum value the structural
509 		 * size field can natively support.
510 		 */
511 		extra_bytes = offsetof(typeof(*param), set_program_ex) +
512 			sizeof(param->set_program_ex);
513 		max_size = min_t(size_t, ec->ec_dev->max_request - extra_bytes,
514 				 type_max(typeof(param->set_program_ex.size)));
515 	}
516 
517 	msg = alloc_lightbar_cmd_msg(ec);
518 	if (!msg)
519 		return -ENOMEM;
520 
521 	ret = lb_throttle();
522 	if (ret)
523 		goto exit;
524 	param = (struct ec_params_lightbar *)msg->data;
525 	msg->insize = 0;
526 
527 	if (lb_version < 3) {
528 		dev_info(dev, "Copying %zu byte program to EC", count);
529 
530 		param->cmd = LIGHTBAR_CMD_SET_PROGRAM;
531 
532 		param->set_program.size = count;
533 		memcpy(param->set_program.data, buf, count);
534 
535 		/*
536 		 * We need to set the message size manually or else it will use
537 		 * EC_LB_PROG_LEN. This might be too long, and the program
538 		 * is unlikely to use all of the space.
539 		 */
540 		msg->outsize = count + extra_bytes;
541 
542 		ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
543 		if (ret < 0)
544 			goto exit;
545 	} else {
546 		size_t offset = 0;
547 		size_t payload = 0;
548 
549 		param->cmd = LIGHTBAR_CMD_SET_PROGRAM_EX;
550 		while (offset < count) {
551 			payload = min(max_size, count - offset);
552 			param->set_program_ex.offset = offset;
553 			param->set_program_ex.size = payload;
554 			memcpy(param->set_program_ex.data, &buf[offset], payload);
555 			msg->outsize = payload + extra_bytes;
556 
557 			ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
558 			if (ret < 0)
559 				goto exit;
560 			offset += payload;
561 		}
562 	}
563 	ret = count;
564 exit:
565 	kfree(msg);
566 
567 	return ret;
568 }
569 
userspace_control_show(struct device * dev,struct device_attribute * attr,char * buf)570 static ssize_t userspace_control_show(struct device *dev,
571 				      struct device_attribute *attr,
572 				      char *buf)
573 {
574 	return sysfs_emit(buf, "%d\n", userspace_control);
575 }
576 
userspace_control_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)577 static ssize_t userspace_control_store(struct device *dev,
578 				       struct device_attribute *attr,
579 				       const char *buf,
580 				       size_t count)
581 {
582 	bool enable;
583 	int ret;
584 
585 	ret = kstrtobool(buf, &enable);
586 	if (ret < 0)
587 		return ret;
588 
589 	userspace_control = enable;
590 
591 	return count;
592 }
593 
594 /* Module initialization */
595 
596 static DEVICE_ATTR_RW(interval_msec);
597 static DEVICE_ATTR_RO(num_segments);
598 static DEVICE_ATTR_RO(version);
599 static DEVICE_ATTR_WO(brightness);
600 static DEVICE_ATTR_WO(led_rgb);
601 static DEVICE_ATTR_RW(sequence);
602 static DEVICE_ATTR_WO(program);
603 static DEVICE_ATTR_RW(userspace_control);
604 
605 static struct attribute *__lb_cmds_attrs[] = {
606 	&dev_attr_interval_msec.attr,
607 	&dev_attr_num_segments.attr,
608 	&dev_attr_version.attr,
609 	&dev_attr_brightness.attr,
610 	&dev_attr_led_rgb.attr,
611 	&dev_attr_sequence.attr,
612 	&dev_attr_program.attr,
613 	&dev_attr_userspace_control.attr,
614 	NULL,
615 };
616 
617 static const struct attribute_group cros_ec_lightbar_attr_group = {
618 	.name = "lightbar",
619 	.attrs = __lb_cmds_attrs,
620 };
621 
cros_ec_lightbar_probe(struct platform_device * pd)622 static int cros_ec_lightbar_probe(struct platform_device *pd)
623 {
624 	struct cros_ec_dev *ec_dev = dev_get_drvdata(pd->dev.parent);
625 	struct cros_ec_platform *pdata = dev_get_platdata(ec_dev->dev);
626 	struct device *dev = &pd->dev;
627 	int ret;
628 
629 	/*
630 	 * Only instantiate the lightbar if the EC name is 'cros_ec'. Other EC
631 	 * devices like 'cros_pd' doesn't have a lightbar.
632 	 */
633 	if (strcmp(pdata->ec_name, CROS_EC_DEV_NAME) != 0)
634 		return -ENODEV;
635 
636 	/*
637 	 * Ask then for the lightbar version, if it's 0 then the 'cros_ec'
638 	 * doesn't have a lightbar.
639 	 */
640 	if (!get_lightbar_version(ec_dev, &lb_version, NULL))
641 		return -ENODEV;
642 
643 	/* Take control of the lightbar from the EC. */
644 	has_manual_suspend = (lb_manual_suspend_ctrl(ec_dev, 1) != -EINVAL);
645 
646 	ret = sysfs_create_group(&ec_dev->class_dev.kobj,
647 				 &cros_ec_lightbar_attr_group);
648 	if (ret < 0)
649 		dev_err(dev, "failed to create %s attributes. err=%d\n",
650 			cros_ec_lightbar_attr_group.name, ret);
651 
652 	return ret;
653 }
654 
cros_ec_lightbar_remove(struct platform_device * pd)655 static void cros_ec_lightbar_remove(struct platform_device *pd)
656 {
657 	struct cros_ec_dev *ec_dev = dev_get_drvdata(pd->dev.parent);
658 
659 	sysfs_remove_group(&ec_dev->class_dev.kobj,
660 			   &cros_ec_lightbar_attr_group);
661 
662 	/* Let the EC take over the lightbar again. */
663 	if (has_manual_suspend)
664 		lb_manual_suspend_ctrl(ec_dev, 0);
665 }
666 
cros_ec_lightbar_resume(struct device * dev)667 static int __maybe_unused cros_ec_lightbar_resume(struct device *dev)
668 {
669 	struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent);
670 
671 	if (userspace_control || !has_manual_suspend)
672 		return 0;
673 
674 	return lb_send_empty_cmd(ec_dev, LIGHTBAR_CMD_RESUME);
675 }
676 
cros_ec_lightbar_suspend(struct device * dev)677 static int __maybe_unused cros_ec_lightbar_suspend(struct device *dev)
678 {
679 	struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent);
680 
681 	if (userspace_control || !has_manual_suspend)
682 		return 0;
683 
684 	return lb_send_empty_cmd(ec_dev, LIGHTBAR_CMD_SUSPEND);
685 }
686 
687 static SIMPLE_DEV_PM_OPS(cros_ec_lightbar_pm_ops,
688 			 cros_ec_lightbar_suspend, cros_ec_lightbar_resume);
689 
690 static const struct platform_device_id cros_ec_lightbar_id[] = {
691 	{ .name = DRV_NAME },
692 	{ }
693 };
694 MODULE_DEVICE_TABLE(platform, cros_ec_lightbar_id);
695 
696 static struct platform_driver cros_ec_lightbar_driver = {
697 	.driver = {
698 		.name = DRV_NAME,
699 		.pm = &cros_ec_lightbar_pm_ops,
700 		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
701 	},
702 	.probe = cros_ec_lightbar_probe,
703 	.remove = cros_ec_lightbar_remove,
704 	.id_table = cros_ec_lightbar_id,
705 };
706 
707 module_platform_driver(cros_ec_lightbar_driver);
708 
709 MODULE_LICENSE("GPL");
710 MODULE_DESCRIPTION("Expose the Chromebook Pixel's lightbar to userspace");
711