// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2016-2017 Linaro Ltd., Rob Herring <robh@kernel.org> * * Based on drivers/spmi/spmi.c: * Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
*/
/* Only a single slave device is currently supported. */ if (ctrl->serdev) {
dev_err(&serdev->dev, "controller busy\n"); return -EBUSY;
}
ctrl->serdev = serdev;
err = device_add(&serdev->dev); if (err < 0) {
dev_err(&serdev->dev, "Failed to add serdev: %d\n", err); goto err_clear_serdev;
}
/** * serdev_device_write_buf() - write data asynchronously * @serdev: serdev device * @buf: data to be written * @count: number of bytes to write * * Write data to the device asynchronously. * * Note that any accepted data has only been buffered by the controller; use * serdev_device_wait_until_sent() to make sure the controller write buffer * has actually been emptied. * * Return: The number of bytes written (less than count if not enough room in * the write buffer), or a negative errno on errors.
*/ int serdev_device_write_buf(struct serdev_device *serdev, const u8 *buf, size_t count)
{ struct serdev_controller *ctrl = serdev->ctrl;
if (!ctrl || !ctrl->ops->write_buf) return -EINVAL;
/** * serdev_device_write() - write data synchronously * @serdev: serdev device * @buf: data to be written * @count: number of bytes to write * @timeout: timeout in jiffies, or 0 to wait indefinitely * * Write data to the device synchronously by repeatedly calling * serdev_device_write() until the controller has accepted all data (unless * interrupted by a timeout or a signal). * * Note that any accepted data has only been buffered by the controller; use * serdev_device_wait_until_sent() to make sure the controller write buffer * has actually been emptied. * * Note that this function depends on serdev_device_write_wakeup() being * called in the serdev driver write_wakeup() callback. * * Return: The number of bytes written (less than count if interrupted), * -ETIMEDOUT or -ERESTARTSYS if interrupted before any bytes were written, or * a negative errno on errors.
*/
ssize_t serdev_device_write(struct serdev_device *serdev, const u8 *buf,
size_t count, long timeout)
{ struct serdev_controller *ctrl = serdev->ctrl;
size_t written = 0;
ssize_t ret;
if (!ctrl || !ctrl->ops->write_buf || !serdev->ops->write_wakeup) return -EINVAL;
if (timeout == 0)
timeout = MAX_SCHEDULE_TIMEOUT;
mutex_lock(&serdev->write_lock); do {
reinit_completion(&serdev->write_comp);
ret = ctrl->ops->write_buf(ctrl, buf, count); if (ret < 0) break;
written += ret;
buf += ret;
count -= ret;
if (count == 0) break;
timeout = wait_for_completion_interruptible_timeout(&serdev->write_comp,
timeout);
} while (timeout > 0);
mutex_unlock(&serdev->write_lock);
if (ret < 0) return ret;
if (timeout <= 0 && written == 0) { if (timeout == -ERESTARTSYS) return -ERESTARTSYS; else return -ETIMEDOUT;
}
/** * serdev_device_alloc() - Allocate a new serdev device * @ctrl: associated controller * * Caller is responsible for either calling serdev_device_add() to add the * newly allocated controller, or calling serdev_device_put() to discard it.
*/ struct serdev_device *serdev_device_alloc(struct serdev_controller *ctrl)
{ struct serdev_device *serdev;
serdev = kzalloc(sizeof(*serdev), GFP_KERNEL); if (!serdev) return NULL;
/** * serdev_controller_alloc() - Allocate a new serdev controller * @host: serial port hardware controller device * @parent: parent device * @size: size of private data * * Caller is responsible for either calling serdev_controller_add() to add the * newly allocated controller, or calling serdev_controller_put() to discard it. * The allocated private data region may be accessed via * serdev_controller_get_drvdata()
*/ struct serdev_controller *serdev_controller_alloc(struct device *host, struct device *parent,
size_t size)
{ struct serdev_controller *ctrl; int id;
if (WARN_ON(!parent)) return NULL;
ctrl = kzalloc(sizeof(*ctrl) + size, GFP_KERNEL); if (!ctrl) return NULL;
id = ida_alloc(&ctrl_ida, GFP_KERNEL); if (id < 0) {
dev_err(parent, "unable to allocate serdev controller identifier.\n"); goto err_free;
}
err = serdev_device_add(serdev); if (err) {
dev_err(&serdev->dev, "failure adding device. status %pe\n",
ERR_PTR(err));
serdev_device_put(serdev);
} else
found = true;
} if (!found) return -ENODEV;
return 0;
}
#ifdef CONFIG_ACPI
#define SERDEV_ACPI_MAX_SCAN_DEPTH 32
struct acpi_serdev_lookup {
acpi_handle device_handle;
acpi_handle controller_handle; int n; int index;
};
/** * serdev_acpi_get_uart_resource - Gets UARTSerialBus resource if type matches * @ares: ACPI resource * @uart: Pointer to UARTSerialBus resource will be returned here * * Checks if the given ACPI resource is of type UARTSerialBus. * In this case, returns a pointer to it to the caller. * * Return: True if resource type is of UARTSerialBus, otherwise false.
*/ bool serdev_acpi_get_uart_resource(struct acpi_resource *ares, struct acpi_resource_uart_serialbus **uart)
{ struct acpi_resource_uart_serialbus *sb;
if (ares->type != ACPI_RESOURCE_TYPE_SERIAL_BUS) returnfalse;
sb = &ares->data.uart_serial_bus; if (sb->type != ACPI_RESOURCE_SERIAL_TYPE_UART) returnfalse;
if (acpi_bus_get_status(adev) || !adev->status.present) return -EINVAL;
/* Look for UARTSerialBusV2 resource */
lookup.index = -1; // we only care for the last device
ret = acpi_serdev_do_lookup(adev, &lookup); if (ret) return ret;
/* * Apple machines provide an empty resource template, so on those * machines just look for immediate children with a "baud" property * (from the _DSM method) instead.
*/ if (!lookup.controller_handle && x86_apple_machine &&
!acpi_dev_get_property(adev, "baud", ACPI_TYPE_BUFFER, NULL))
acpi_get_parent(adev->handle, &lookup.controller_handle);
/* Make sure controller and ResourceSource handle match */ if (!device_match_acpi_handle(ctrl->host, lookup.controller_handle)) return -ENODEV;
if (!has_acpi_companion(ctrl->host)) return -ENODEV;
/* * Skip registration on boards where the ACPI tables are known to * contain buggy devices. Note serdev_controller_add() must still * succeed in this case, so that the proper serdev devices can be * added "manually" later.
*/
ret = acpi_quirk_skip_serdev_enumeration(ctrl->host, &skip); if (ret) return ret; if (skip) return 0;
status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
SERDEV_ACPI_MAX_SCAN_DEPTH,
acpi_serdev_add_device, NULL, ctrl, NULL); if (ACPI_FAILURE(status))
dev_warn(&ctrl->dev, "failed to enumerate serdev slaves\n");
/** * serdev_controller_add() - Add an serdev controller * @ctrl: controller to be registered. * * Register a controller previously allocated via serdev_controller_alloc() with * the serdev core.
*/ int serdev_controller_add(struct serdev_controller *ctrl)
{ int ret_of, ret_acpi, ret;
/* Can't register until after driver model init */ if (WARN_ON(!is_registered)) return -EAGAIN;
ret = device_add(&ctrl->dev); if (ret) return ret;
pm_runtime_enable(&ctrl->dev);
ret_of = of_serdev_register_devices(ctrl);
ret_acpi = acpi_serdev_register_devices(ctrl); if (ret_of && ret_acpi) {
dev_dbg(&ctrl->dev, "no devices registered: of:%pe acpi:%pe\n",
ERR_PTR(ret_of), ERR_PTR(ret_acpi));
ret = -ENODEV; goto err_rpm_disable;
}
/* Remove a device associated with a controller */ staticint serdev_remove_device(struct device *dev, void *data)
{ struct serdev_device *serdev = to_serdev_device(dev); if (dev->type == &serdev_device_type)
serdev_device_remove(serdev); return 0;
}
/** * serdev_controller_remove(): remove an serdev controller * @ctrl: controller to remove * * Remove a serdev controller. Caller is responsible for calling * serdev_controller_put() to discard the allocated controller.
*/ void serdev_controller_remove(struct serdev_controller *ctrl)
{ if (!ctrl) return;
/** * __serdev_device_driver_register() - Register client driver with serdev core * @sdrv: client driver to be associated with client-device. * @owner: client driver owner to set. * * This API will register the client driver with the serdev framework. * It is typically called from the driver's module-init function.
*/ int __serdev_device_driver_register(struct serdev_device_driver *sdrv, struct module *owner)
{
sdrv->driver.bus = &serdev_bus_type;
sdrv->driver.owner = owner;
/* force drivers to async probe so I/O is possible in probe */
sdrv->driver.probe_type = PROBE_PREFER_ASYNCHRONOUS;
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.13Bemerkung:
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.