aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/tty/serdev/core.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2017-11-14 00:05:31 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2017-11-14 00:05:31 -0500
commitfb0255fb2941ef6f21742b2bc146d6b9aef4fedc (patch)
tree8334f3485152b1c887ddfe04ba9a95c8a704481c /drivers/tty/serdev/core.c
parent449fcf3ab0baf3dde9952385e6789f2ca10c3980 (diff)
parent57f5d648c45c3d40a3257c06629c14fd53c383bc (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.c152
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
50static int serdev_device_match(struct device *dev, struct device_driver *drv) 43static 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
56static int serdev_uevent(struct device *dev, struct kobj_uevent_env *env) 52static 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 */
66int serdev_device_add(struct serdev_device *serdev) 68int 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
82err_device_add: 92 return 0;
93
94err_clear_serdev:
95 ctrl->serdev = NULL;
83 return err; 96 return err;
84} 97}
85EXPORT_SYMBOL_GPL(serdev_device_add); 98EXPORT_SYMBOL_GPL(serdev_device_add);
@@ -90,7 +103,10 @@ EXPORT_SYMBOL_GPL(serdev_device_add);
90 */ 103 */
91void serdev_device_remove(struct serdev_device *serdev) 104void 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}
95EXPORT_SYMBOL_GPL(serdev_device_remove); 111EXPORT_SYMBOL_GPL(serdev_device_remove);
96 112
@@ -260,6 +276,12 @@ static int serdev_drv_remove(struct device *dev)
260static ssize_t modalias_show(struct device *dev, 276static 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}
265DEVICE_ATTR_RO(modalias); 287DEVICE_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
374err_free:
375 kfree(ctrl);
376
377 return NULL;
352} 378}
353EXPORT_SYMBOL_GPL(serdev_controller_alloc); 379EXPORT_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
415static 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
445static 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
457static 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
477static 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 */
395int serdev_controller_add(struct serdev_controller *ctrl) 490int 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);