diff options
Diffstat (limited to 'drivers/tty/serdev')
-rw-r--r-- | drivers/tty/serdev/Kconfig | 8 | ||||
-rw-r--r-- | drivers/tty/serdev/core.c | 152 | ||||
-rw-r--r-- | drivers/tty/serdev/serdev-ttyport.c | 31 |
3 files changed, 152 insertions, 39 deletions
diff --git a/drivers/tty/serdev/Kconfig b/drivers/tty/serdev/Kconfig index cdc6b820cf93..1dbc8352e027 100644 --- a/drivers/tty/serdev/Kconfig +++ b/drivers/tty/serdev/Kconfig | |||
@@ -6,11 +6,19 @@ menuconfig SERIAL_DEV_BUS | |||
6 | help | 6 | help |
7 | Core support for devices connected via a serial port. | 7 | Core support for devices connected via a serial port. |
8 | 8 | ||
9 | Note that you typically also want to enable TTY port controller support. | ||
10 | |||
9 | if SERIAL_DEV_BUS | 11 | if SERIAL_DEV_BUS |
10 | 12 | ||
11 | config SERIAL_DEV_CTRL_TTYPORT | 13 | config SERIAL_DEV_CTRL_TTYPORT |
12 | bool "Serial device TTY port controller" | 14 | bool "Serial device TTY port controller" |
15 | help | ||
16 | Say Y here if you want to use the Serial device bus with common TTY | ||
17 | drivers (e.g. serial drivers). | ||
18 | |||
19 | If unsure, say Y. | ||
13 | depends on TTY | 20 | depends on TTY |
14 | depends on SERIAL_DEV_BUS != m | 21 | depends on SERIAL_DEV_BUS != m |
22 | default y | ||
15 | 23 | ||
16 | endif | 24 | endif |
diff --git a/drivers/tty/serdev/core.c b/drivers/tty/serdev/core.c index c68fb3a8ea1c..1bef39828ca7 100644 --- a/drivers/tty/serdev/core.c +++ b/drivers/tty/serdev/core.c | |||
@@ -1,19 +1,12 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
1 | /* | 2 | /* |
2 | * Copyright (C) 2016-2017 Linaro Ltd., Rob Herring <robh@kernel.org> | 3 | * Copyright (C) 2016-2017 Linaro Ltd., Rob Herring <robh@kernel.org> |
3 | * | 4 | * |
4 | * Based on drivers/spmi/spmi.c: | 5 | * Based on drivers/spmi/spmi.c: |
5 | * Copyright (c) 2012-2015, The Linux Foundation. All rights reserved. | 6 | * Copyright (c) 2012-2015, The Linux Foundation. All rights reserved. |
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 and | ||
9 | * only version 2 as published by the Free Software Foundation. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | */ | 7 | */ |
16 | 8 | ||
9 | #include <linux/acpi.h> | ||
17 | #include <linux/errno.h> | 10 | #include <linux/errno.h> |
18 | #include <linux/idr.h> | 11 | #include <linux/idr.h> |
19 | #include <linux/kernel.h> | 12 | #include <linux/kernel.h> |
@@ -49,13 +42,22 @@ static const struct device_type serdev_ctrl_type = { | |||
49 | 42 | ||
50 | static int serdev_device_match(struct device *dev, struct device_driver *drv) | 43 | static int serdev_device_match(struct device *dev, struct device_driver *drv) |
51 | { | 44 | { |
52 | /* TODO: ACPI and platform matching */ | 45 | /* TODO: platform matching */ |
46 | if (acpi_driver_match_device(dev, drv)) | ||
47 | return 1; | ||
48 | |||
53 | return of_driver_match_device(dev, drv); | 49 | return of_driver_match_device(dev, drv); |
54 | } | 50 | } |
55 | 51 | ||
56 | static int serdev_uevent(struct device *dev, struct kobj_uevent_env *env) | 52 | static int serdev_uevent(struct device *dev, struct kobj_uevent_env *env) |
57 | { | 53 | { |
58 | /* TODO: ACPI and platform modalias */ | 54 | int rc; |
55 | |||
56 | /* TODO: platform modalias */ | ||
57 | rc = acpi_device_uevent_modalias(dev, env); | ||
58 | if (rc != -ENODEV) | ||
59 | return rc; | ||
60 | |||
59 | return of_device_uevent_modalias(dev, env); | 61 | return of_device_uevent_modalias(dev, env); |
60 | } | 62 | } |
61 | 63 | ||
@@ -65,21 +67,32 @@ static int serdev_uevent(struct device *dev, struct kobj_uevent_env *env) | |||
65 | */ | 67 | */ |
66 | int serdev_device_add(struct serdev_device *serdev) | 68 | int serdev_device_add(struct serdev_device *serdev) |
67 | { | 69 | { |
70 | struct serdev_controller *ctrl = serdev->ctrl; | ||
68 | struct device *parent = serdev->dev.parent; | 71 | struct device *parent = serdev->dev.parent; |
69 | int err; | 72 | int err; |
70 | 73 | ||
71 | dev_set_name(&serdev->dev, "%s-%d", dev_name(parent), serdev->nr); | 74 | dev_set_name(&serdev->dev, "%s-%d", dev_name(parent), serdev->nr); |
72 | 75 | ||
76 | /* Only a single slave device is currently supported. */ | ||
77 | if (ctrl->serdev) { | ||
78 | dev_err(&serdev->dev, "controller busy\n"); | ||
79 | return -EBUSY; | ||
80 | } | ||
81 | ctrl->serdev = serdev; | ||
82 | |||
73 | err = device_add(&serdev->dev); | 83 | err = device_add(&serdev->dev); |
74 | if (err < 0) { | 84 | if (err < 0) { |
75 | dev_err(&serdev->dev, "Can't add %s, status %d\n", | 85 | dev_err(&serdev->dev, "Can't add %s, status %d\n", |
76 | dev_name(&serdev->dev), err); | 86 | dev_name(&serdev->dev), err); |
77 | goto err_device_add; | 87 | goto err_clear_serdev; |
78 | } | 88 | } |
79 | 89 | ||
80 | dev_dbg(&serdev->dev, "device %s registered\n", dev_name(&serdev->dev)); | 90 | dev_dbg(&serdev->dev, "device %s registered\n", dev_name(&serdev->dev)); |
81 | 91 | ||
82 | err_device_add: | 92 | return 0; |
93 | |||
94 | err_clear_serdev: | ||
95 | ctrl->serdev = NULL; | ||
83 | return err; | 96 | return err; |
84 | } | 97 | } |
85 | EXPORT_SYMBOL_GPL(serdev_device_add); | 98 | EXPORT_SYMBOL_GPL(serdev_device_add); |
@@ -90,7 +103,10 @@ EXPORT_SYMBOL_GPL(serdev_device_add); | |||
90 | */ | 103 | */ |
91 | void serdev_device_remove(struct serdev_device *serdev) | 104 | void serdev_device_remove(struct serdev_device *serdev) |
92 | { | 105 | { |
106 | struct serdev_controller *ctrl = serdev->ctrl; | ||
107 | |||
93 | device_unregister(&serdev->dev); | 108 | device_unregister(&serdev->dev); |
109 | ctrl->serdev = NULL; | ||
94 | } | 110 | } |
95 | EXPORT_SYMBOL_GPL(serdev_device_remove); | 111 | EXPORT_SYMBOL_GPL(serdev_device_remove); |
96 | 112 | ||
@@ -260,6 +276,12 @@ static int serdev_drv_remove(struct device *dev) | |||
260 | static ssize_t modalias_show(struct device *dev, | 276 | static ssize_t modalias_show(struct device *dev, |
261 | struct device_attribute *attr, char *buf) | 277 | struct device_attribute *attr, char *buf) |
262 | { | 278 | { |
279 | int len; | ||
280 | |||
281 | len = acpi_device_modalias(dev, buf, PAGE_SIZE - 1); | ||
282 | if (len != -ENODEV) | ||
283 | return len; | ||
284 | |||
263 | return of_device_modalias(dev, buf, PAGE_SIZE); | 285 | return of_device_modalias(dev, buf, PAGE_SIZE); |
264 | } | 286 | } |
265 | DEVICE_ATTR_RO(modalias); | 287 | DEVICE_ATTR_RO(modalias); |
@@ -295,7 +317,6 @@ struct serdev_device *serdev_device_alloc(struct serdev_controller *ctrl) | |||
295 | return NULL; | 317 | return NULL; |
296 | 318 | ||
297 | serdev->ctrl = ctrl; | 319 | serdev->ctrl = ctrl; |
298 | ctrl->serdev = serdev; | ||
299 | device_initialize(&serdev->dev); | 320 | device_initialize(&serdev->dev); |
300 | serdev->dev.parent = &ctrl->dev; | 321 | serdev->dev.parent = &ctrl->dev; |
301 | serdev->dev.bus = &serdev_bus_type; | 322 | serdev->dev.bus = &serdev_bus_type; |
@@ -329,26 +350,31 @@ struct serdev_controller *serdev_controller_alloc(struct device *parent, | |||
329 | if (!ctrl) | 350 | if (!ctrl) |
330 | return NULL; | 351 | return NULL; |
331 | 352 | ||
332 | device_initialize(&ctrl->dev); | ||
333 | ctrl->dev.type = &serdev_ctrl_type; | ||
334 | ctrl->dev.bus = &serdev_bus_type; | ||
335 | ctrl->dev.parent = parent; | ||
336 | ctrl->dev.of_node = parent->of_node; | ||
337 | serdev_controller_set_drvdata(ctrl, &ctrl[1]); | ||
338 | |||
339 | id = ida_simple_get(&ctrl_ida, 0, 0, GFP_KERNEL); | 353 | id = ida_simple_get(&ctrl_ida, 0, 0, GFP_KERNEL); |
340 | if (id < 0) { | 354 | if (id < 0) { |
341 | dev_err(parent, | 355 | dev_err(parent, |
342 | "unable to allocate serdev controller identifier.\n"); | 356 | "unable to allocate serdev controller identifier.\n"); |
343 | serdev_controller_put(ctrl); | 357 | goto err_free; |
344 | return NULL; | ||
345 | } | 358 | } |
346 | 359 | ||
347 | ctrl->nr = id; | 360 | ctrl->nr = id; |
361 | |||
362 | device_initialize(&ctrl->dev); | ||
363 | ctrl->dev.type = &serdev_ctrl_type; | ||
364 | ctrl->dev.bus = &serdev_bus_type; | ||
365 | ctrl->dev.parent = parent; | ||
366 | ctrl->dev.of_node = parent->of_node; | ||
367 | serdev_controller_set_drvdata(ctrl, &ctrl[1]); | ||
368 | |||
348 | dev_set_name(&ctrl->dev, "serial%d", id); | 369 | dev_set_name(&ctrl->dev, "serial%d", id); |
349 | 370 | ||
350 | dev_dbg(&ctrl->dev, "allocated controller 0x%p id %d\n", ctrl, id); | 371 | dev_dbg(&ctrl->dev, "allocated controller 0x%p id %d\n", ctrl, id); |
351 | return ctrl; | 372 | return ctrl; |
373 | |||
374 | err_free: | ||
375 | kfree(ctrl); | ||
376 | |||
377 | return NULL; | ||
352 | } | 378 | } |
353 | EXPORT_SYMBOL_GPL(serdev_controller_alloc); | 379 | EXPORT_SYMBOL_GPL(serdev_controller_alloc); |
354 | 380 | ||
@@ -385,6 +411,75 @@ static int of_serdev_register_devices(struct serdev_controller *ctrl) | |||
385 | return 0; | 411 | return 0; |
386 | } | 412 | } |
387 | 413 | ||
414 | #ifdef CONFIG_ACPI | ||
415 | static acpi_status acpi_serdev_register_device(struct serdev_controller *ctrl, | ||
416 | struct acpi_device *adev) | ||
417 | { | ||
418 | struct serdev_device *serdev = NULL; | ||
419 | int err; | ||
420 | |||
421 | if (acpi_bus_get_status(adev) || !adev->status.present || | ||
422 | acpi_device_enumerated(adev)) | ||
423 | return AE_OK; | ||
424 | |||
425 | serdev = serdev_device_alloc(ctrl); | ||
426 | if (!serdev) { | ||
427 | dev_err(&ctrl->dev, "failed to allocate serdev device for %s\n", | ||
428 | dev_name(&adev->dev)); | ||
429 | return AE_NO_MEMORY; | ||
430 | } | ||
431 | |||
432 | ACPI_COMPANION_SET(&serdev->dev, adev); | ||
433 | acpi_device_set_enumerated(adev); | ||
434 | |||
435 | err = serdev_device_add(serdev); | ||
436 | if (err) { | ||
437 | dev_err(&serdev->dev, | ||
438 | "failure adding ACPI serdev device. status %d\n", err); | ||
439 | serdev_device_put(serdev); | ||
440 | } | ||
441 | |||
442 | return AE_OK; | ||
443 | } | ||
444 | |||
445 | static acpi_status acpi_serdev_add_device(acpi_handle handle, u32 level, | ||
446 | void *data, void **return_value) | ||
447 | { | ||
448 | struct serdev_controller *ctrl = data; | ||
449 | struct acpi_device *adev; | ||
450 | |||
451 | if (acpi_bus_get_device(handle, &adev)) | ||
452 | return AE_OK; | ||
453 | |||
454 | return acpi_serdev_register_device(ctrl, adev); | ||
455 | } | ||
456 | |||
457 | static int acpi_serdev_register_devices(struct serdev_controller *ctrl) | ||
458 | { | ||
459 | acpi_status status; | ||
460 | acpi_handle handle; | ||
461 | |||
462 | handle = ACPI_HANDLE(ctrl->dev.parent); | ||
463 | if (!handle) | ||
464 | return -ENODEV; | ||
465 | |||
466 | status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1, | ||
467 | acpi_serdev_add_device, NULL, ctrl, NULL); | ||
468 | if (ACPI_FAILURE(status)) | ||
469 | dev_dbg(&ctrl->dev, "failed to enumerate serdev slaves\n"); | ||
470 | |||
471 | if (!ctrl->serdev) | ||
472 | return -ENODEV; | ||
473 | |||
474 | return 0; | ||
475 | } | ||
476 | #else | ||
477 | static inline int acpi_serdev_register_devices(struct serdev_controller *ctrl) | ||
478 | { | ||
479 | return -ENODEV; | ||
480 | } | ||
481 | #endif /* CONFIG_ACPI */ | ||
482 | |||
388 | /** | 483 | /** |
389 | * serdev_controller_add() - Add an serdev controller | 484 | * serdev_controller_add() - Add an serdev controller |
390 | * @ctrl: controller to be registered. | 485 | * @ctrl: controller to be registered. |
@@ -394,7 +489,7 @@ static int of_serdev_register_devices(struct serdev_controller *ctrl) | |||
394 | */ | 489 | */ |
395 | int serdev_controller_add(struct serdev_controller *ctrl) | 490 | int serdev_controller_add(struct serdev_controller *ctrl) |
396 | { | 491 | { |
397 | int ret; | 492 | int ret_of, ret_acpi, ret; |
398 | 493 | ||
399 | /* Can't register until after driver model init */ | 494 | /* Can't register until after driver model init */ |
400 | if (WARN_ON(!is_registered)) | 495 | if (WARN_ON(!is_registered)) |
@@ -404,9 +499,14 @@ int serdev_controller_add(struct serdev_controller *ctrl) | |||
404 | if (ret) | 499 | if (ret) |
405 | return ret; | 500 | return ret; |
406 | 501 | ||
407 | ret = of_serdev_register_devices(ctrl); | 502 | ret_of = of_serdev_register_devices(ctrl); |
408 | if (ret) | 503 | ret_acpi = acpi_serdev_register_devices(ctrl); |
504 | if (ret_of && ret_acpi) { | ||
505 | dev_dbg(&ctrl->dev, "no devices registered: of:%d acpi:%d\n", | ||
506 | ret_of, ret_acpi); | ||
507 | ret = -ENODEV; | ||
409 | goto out_dev_del; | 508 | goto out_dev_del; |
509 | } | ||
410 | 510 | ||
411 | dev_dbg(&ctrl->dev, "serdev%d registered: dev:%p\n", | 511 | dev_dbg(&ctrl->dev, "serdev%d registered: dev:%p\n", |
412 | ctrl->nr, &ctrl->dev); | 512 | ctrl->nr, &ctrl->dev); |
diff --git a/drivers/tty/serdev/serdev-ttyport.c b/drivers/tty/serdev/serdev-ttyport.c index 302018d67efa..ce7ad0acee7a 100644 --- a/drivers/tty/serdev/serdev-ttyport.c +++ b/drivers/tty/serdev/serdev-ttyport.c | |||
@@ -1,14 +1,6 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
1 | /* | 2 | /* |
2 | * Copyright (C) 2016-2017 Linaro Ltd., Rob Herring <robh@kernel.org> | 3 | * Copyright (C) 2016-2017 Linaro Ltd., Rob Herring <robh@kernel.org> |
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 and | ||
6 | * only version 2 as published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | */ | 4 | */ |
13 | #include <linux/kernel.h> | 5 | #include <linux/kernel.h> |
14 | #include <linux/serdev.h> | 6 | #include <linux/serdev.h> |
@@ -96,16 +88,21 @@ static int ttyport_open(struct serdev_controller *ctrl) | |||
96 | struct serport *serport = serdev_controller_get_drvdata(ctrl); | 88 | struct serport *serport = serdev_controller_get_drvdata(ctrl); |
97 | struct tty_struct *tty; | 89 | struct tty_struct *tty; |
98 | struct ktermios ktermios; | 90 | struct ktermios ktermios; |
91 | int ret; | ||
99 | 92 | ||
100 | tty = tty_init_dev(serport->tty_drv, serport->tty_idx); | 93 | tty = tty_init_dev(serport->tty_drv, serport->tty_idx); |
101 | if (IS_ERR(tty)) | 94 | if (IS_ERR(tty)) |
102 | return PTR_ERR(tty); | 95 | return PTR_ERR(tty); |
103 | serport->tty = tty; | 96 | serport->tty = tty; |
104 | 97 | ||
105 | if (tty->ops->open) | 98 | if (!tty->ops->open || !tty->ops->close) { |
106 | tty->ops->open(serport->tty, NULL); | 99 | ret = -ENODEV; |
107 | else | 100 | goto err_unlock; |
108 | tty_port_open(serport->port, tty, NULL); | 101 | } |
102 | |||
103 | ret = tty->ops->open(serport->tty, NULL); | ||
104 | if (ret) | ||
105 | goto err_close; | ||
109 | 106 | ||
110 | /* Bring the UART into a known 8 bits no parity hw fc state */ | 107 | /* Bring the UART into a known 8 bits no parity hw fc state */ |
111 | ktermios = tty->termios; | 108 | ktermios = tty->termios; |
@@ -122,6 +119,14 @@ static int ttyport_open(struct serdev_controller *ctrl) | |||
122 | 119 | ||
123 | tty_unlock(serport->tty); | 120 | tty_unlock(serport->tty); |
124 | return 0; | 121 | return 0; |
122 | |||
123 | err_close: | ||
124 | tty->ops->close(tty, NULL); | ||
125 | err_unlock: | ||
126 | tty_unlock(tty); | ||
127 | tty_release_struct(tty, serport->tty_idx); | ||
128 | |||
129 | return ret; | ||
125 | } | 130 | } |
126 | 131 | ||
127 | static void ttyport_close(struct serdev_controller *ctrl) | 132 | static void ttyport_close(struct serdev_controller *ctrl) |