diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2017-11-14 00:05:31 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-11-14 00:05:31 -0500 |
commit | fb0255fb2941ef6f21742b2bc146d6b9aef4fedc (patch) | |
tree | 8334f3485152b1c887ddfe04ba9a95c8a704481c /drivers/tty/serdev/core.c | |
parent | 449fcf3ab0baf3dde9952385e6789f2ca10c3980 (diff) | |
parent | 57f5d648c45c3d40a3257c06629c14fd53c383bc (diff) |
Merge tag 'tty-4.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty
Pull tty/serial updates from Greg KH:
"Here is the big tty/serial driver pull request for 4.15-rc1.
Lots of serial driver updates in here, some small vt cleanups, and a
raft of SPDX and license boilerplate cleanups, messing up the diffstat
a bit.
Nothing major, with no realy functional changes except better hardware
support for some platforms.
All of these have been in linux-next for a while with no reported
issues"
* tag 'tty-4.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty: (110 commits)
tty: ehv_bytechan: fix spelling mistake
tty: serial: meson: allow baud-rates lower than 9600
serial: 8250_fintek: Fix crash with baud rate B0
serial: 8250_fintek: Disable delays for ports != 0
serial: 8250_fintek: Return -EINVAL on invalid configuration
tty: Remove redundant license text
tty: serdev: Remove redundant license text
tty: hvc: Remove redundant license text
tty: serial: Remove redundant license text
tty: add SPDX identifiers to all remaining files in drivers/tty/
tty: serial: jsm: remove redundant pointer ts
tty: serial: jsm: add space before the open parenthesis '('
tty: serial: jsm: fix coding style
tty: serial: jsm: delete space between function name and '('
tty: serial: jsm: add blank line after declarations
tty: serial: jsm: change the type of local variable
tty: serial: imx: remove dead code imx_dma_rxint
tty: serial: imx: disable ageing timer interrupt if dma in use
serial: 8250: fix potential deadlock in rs485-mode
serial: m32r_sio: Drop redundant .data assignment
...
Diffstat (limited to 'drivers/tty/serdev/core.c')
-rw-r--r-- | drivers/tty/serdev/core.c | 152 |
1 files changed, 126 insertions, 26 deletions
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); |