162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2016-2017 Linaro Ltd., Rob Herring <robh@kernel.org> 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Based on drivers/spmi/spmi.c: 662306a36Sopenharmony_ci * Copyright (c) 2012-2015, The Linux Foundation. All rights reserved. 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/acpi.h> 1062306a36Sopenharmony_ci#include <linux/errno.h> 1162306a36Sopenharmony_ci#include <linux/idr.h> 1262306a36Sopenharmony_ci#include <linux/kernel.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/of.h> 1562306a36Sopenharmony_ci#include <linux/of_device.h> 1662306a36Sopenharmony_ci#include <linux/pm_domain.h> 1762306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1862306a36Sopenharmony_ci#include <linux/sched.h> 1962306a36Sopenharmony_ci#include <linux/serdev.h> 2062306a36Sopenharmony_ci#include <linux/slab.h> 2162306a36Sopenharmony_ci#include <linux/platform_data/x86/apple.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic bool is_registered; 2462306a36Sopenharmony_cistatic DEFINE_IDA(ctrl_ida); 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic ssize_t modalias_show(struct device *dev, 2762306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci int len; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci len = acpi_device_modalias(dev, buf, PAGE_SIZE - 1); 3262306a36Sopenharmony_ci if (len != -ENODEV) 3362306a36Sopenharmony_ci return len; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci return of_device_modalias(dev, buf, PAGE_SIZE); 3662306a36Sopenharmony_ci} 3762306a36Sopenharmony_cistatic DEVICE_ATTR_RO(modalias); 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic struct attribute *serdev_device_attrs[] = { 4062306a36Sopenharmony_ci &dev_attr_modalias.attr, 4162306a36Sopenharmony_ci NULL, 4262306a36Sopenharmony_ci}; 4362306a36Sopenharmony_ciATTRIBUTE_GROUPS(serdev_device); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic int serdev_device_uevent(const struct device *dev, struct kobj_uevent_env *env) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci int rc; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci /* TODO: platform modalias */ 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci rc = acpi_device_uevent_modalias(dev, env); 5262306a36Sopenharmony_ci if (rc != -ENODEV) 5362306a36Sopenharmony_ci return rc; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci return of_device_uevent_modalias(dev, env); 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic void serdev_device_release(struct device *dev) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci struct serdev_device *serdev = to_serdev_device(dev); 6162306a36Sopenharmony_ci kfree(serdev); 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic const struct device_type serdev_device_type = { 6562306a36Sopenharmony_ci .groups = serdev_device_groups, 6662306a36Sopenharmony_ci .uevent = serdev_device_uevent, 6762306a36Sopenharmony_ci .release = serdev_device_release, 6862306a36Sopenharmony_ci}; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic bool is_serdev_device(const struct device *dev) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci return dev->type == &serdev_device_type; 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic void serdev_ctrl_release(struct device *dev) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci struct serdev_controller *ctrl = to_serdev_controller(dev); 7862306a36Sopenharmony_ci ida_simple_remove(&ctrl_ida, ctrl->nr); 7962306a36Sopenharmony_ci kfree(ctrl); 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic const struct device_type serdev_ctrl_type = { 8362306a36Sopenharmony_ci .release = serdev_ctrl_release, 8462306a36Sopenharmony_ci}; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic int serdev_device_match(struct device *dev, struct device_driver *drv) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci if (!is_serdev_device(dev)) 8962306a36Sopenharmony_ci return 0; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci /* TODO: platform matching */ 9262306a36Sopenharmony_ci if (acpi_driver_match_device(dev, drv)) 9362306a36Sopenharmony_ci return 1; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci return of_driver_match_device(dev, drv); 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci/** 9962306a36Sopenharmony_ci * serdev_device_add() - add a device previously constructed via serdev_device_alloc() 10062306a36Sopenharmony_ci * @serdev: serdev_device to be added 10162306a36Sopenharmony_ci */ 10262306a36Sopenharmony_ciint serdev_device_add(struct serdev_device *serdev) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci struct serdev_controller *ctrl = serdev->ctrl; 10562306a36Sopenharmony_ci struct device *parent = serdev->dev.parent; 10662306a36Sopenharmony_ci int err; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci dev_set_name(&serdev->dev, "%s-%d", dev_name(parent), serdev->nr); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci /* Only a single slave device is currently supported. */ 11162306a36Sopenharmony_ci if (ctrl->serdev) { 11262306a36Sopenharmony_ci dev_err(&serdev->dev, "controller busy\n"); 11362306a36Sopenharmony_ci return -EBUSY; 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci ctrl->serdev = serdev; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci err = device_add(&serdev->dev); 11862306a36Sopenharmony_ci if (err < 0) { 11962306a36Sopenharmony_ci dev_err(&serdev->dev, "Can't add %s, status %pe\n", 12062306a36Sopenharmony_ci dev_name(&serdev->dev), ERR_PTR(err)); 12162306a36Sopenharmony_ci goto err_clear_serdev; 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci dev_dbg(&serdev->dev, "device %s registered\n", dev_name(&serdev->dev)); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci return 0; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cierr_clear_serdev: 12962306a36Sopenharmony_ci ctrl->serdev = NULL; 13062306a36Sopenharmony_ci return err; 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(serdev_device_add); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci/** 13562306a36Sopenharmony_ci * serdev_device_remove(): remove an serdev device 13662306a36Sopenharmony_ci * @serdev: serdev_device to be removed 13762306a36Sopenharmony_ci */ 13862306a36Sopenharmony_civoid serdev_device_remove(struct serdev_device *serdev) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci struct serdev_controller *ctrl = serdev->ctrl; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci device_unregister(&serdev->dev); 14362306a36Sopenharmony_ci ctrl->serdev = NULL; 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(serdev_device_remove); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ciint serdev_device_open(struct serdev_device *serdev) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci struct serdev_controller *ctrl = serdev->ctrl; 15062306a36Sopenharmony_ci int ret; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci if (!ctrl || !ctrl->ops->open) 15362306a36Sopenharmony_ci return -EINVAL; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci ret = ctrl->ops->open(ctrl); 15662306a36Sopenharmony_ci if (ret) 15762306a36Sopenharmony_ci return ret; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci ret = pm_runtime_get_sync(&ctrl->dev); 16062306a36Sopenharmony_ci if (ret < 0) { 16162306a36Sopenharmony_ci pm_runtime_put_noidle(&ctrl->dev); 16262306a36Sopenharmony_ci goto err_close; 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci return 0; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cierr_close: 16862306a36Sopenharmony_ci if (ctrl->ops->close) 16962306a36Sopenharmony_ci ctrl->ops->close(ctrl); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci return ret; 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(serdev_device_open); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_civoid serdev_device_close(struct serdev_device *serdev) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci struct serdev_controller *ctrl = serdev->ctrl; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci if (!ctrl || !ctrl->ops->close) 18062306a36Sopenharmony_ci return; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci pm_runtime_put(&ctrl->dev); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci ctrl->ops->close(ctrl); 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(serdev_device_close); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistatic void devm_serdev_device_release(struct device *dev, void *dr) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci serdev_device_close(*(struct serdev_device **)dr); 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ciint devm_serdev_device_open(struct device *dev, struct serdev_device *serdev) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci struct serdev_device **dr; 19662306a36Sopenharmony_ci int ret; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci dr = devres_alloc(devm_serdev_device_release, sizeof(*dr), GFP_KERNEL); 19962306a36Sopenharmony_ci if (!dr) 20062306a36Sopenharmony_ci return -ENOMEM; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci ret = serdev_device_open(serdev); 20362306a36Sopenharmony_ci if (ret) { 20462306a36Sopenharmony_ci devres_free(dr); 20562306a36Sopenharmony_ci return ret; 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci *dr = serdev; 20962306a36Sopenharmony_ci devres_add(dev, dr); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci return 0; 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_serdev_device_open); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_civoid serdev_device_write_wakeup(struct serdev_device *serdev) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci complete(&serdev->write_comp); 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(serdev_device_write_wakeup); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci/** 22262306a36Sopenharmony_ci * serdev_device_write_buf() - write data asynchronously 22362306a36Sopenharmony_ci * @serdev: serdev device 22462306a36Sopenharmony_ci * @buf: data to be written 22562306a36Sopenharmony_ci * @count: number of bytes to write 22662306a36Sopenharmony_ci * 22762306a36Sopenharmony_ci * Write data to the device asynchronously. 22862306a36Sopenharmony_ci * 22962306a36Sopenharmony_ci * Note that any accepted data has only been buffered by the controller; use 23062306a36Sopenharmony_ci * serdev_device_wait_until_sent() to make sure the controller write buffer 23162306a36Sopenharmony_ci * has actually been emptied. 23262306a36Sopenharmony_ci * 23362306a36Sopenharmony_ci * Return: The number of bytes written (less than count if not enough room in 23462306a36Sopenharmony_ci * the write buffer), or a negative errno on errors. 23562306a36Sopenharmony_ci */ 23662306a36Sopenharmony_ciint serdev_device_write_buf(struct serdev_device *serdev, 23762306a36Sopenharmony_ci const unsigned char *buf, size_t count) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci struct serdev_controller *ctrl = serdev->ctrl; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci if (!ctrl || !ctrl->ops->write_buf) 24262306a36Sopenharmony_ci return -EINVAL; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci return ctrl->ops->write_buf(ctrl, buf, count); 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(serdev_device_write_buf); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci/** 24962306a36Sopenharmony_ci * serdev_device_write() - write data synchronously 25062306a36Sopenharmony_ci * @serdev: serdev device 25162306a36Sopenharmony_ci * @buf: data to be written 25262306a36Sopenharmony_ci * @count: number of bytes to write 25362306a36Sopenharmony_ci * @timeout: timeout in jiffies, or 0 to wait indefinitely 25462306a36Sopenharmony_ci * 25562306a36Sopenharmony_ci * Write data to the device synchronously by repeatedly calling 25662306a36Sopenharmony_ci * serdev_device_write() until the controller has accepted all data (unless 25762306a36Sopenharmony_ci * interrupted by a timeout or a signal). 25862306a36Sopenharmony_ci * 25962306a36Sopenharmony_ci * Note that any accepted data has only been buffered by the controller; use 26062306a36Sopenharmony_ci * serdev_device_wait_until_sent() to make sure the controller write buffer 26162306a36Sopenharmony_ci * has actually been emptied. 26262306a36Sopenharmony_ci * 26362306a36Sopenharmony_ci * Note that this function depends on serdev_device_write_wakeup() being 26462306a36Sopenharmony_ci * called in the serdev driver write_wakeup() callback. 26562306a36Sopenharmony_ci * 26662306a36Sopenharmony_ci * Return: The number of bytes written (less than count if interrupted), 26762306a36Sopenharmony_ci * -ETIMEDOUT or -ERESTARTSYS if interrupted before any bytes were written, or 26862306a36Sopenharmony_ci * a negative errno on errors. 26962306a36Sopenharmony_ci */ 27062306a36Sopenharmony_ciint serdev_device_write(struct serdev_device *serdev, 27162306a36Sopenharmony_ci const unsigned char *buf, size_t count, 27262306a36Sopenharmony_ci long timeout) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci struct serdev_controller *ctrl = serdev->ctrl; 27562306a36Sopenharmony_ci int written = 0; 27662306a36Sopenharmony_ci int ret; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci if (!ctrl || !ctrl->ops->write_buf || !serdev->ops->write_wakeup) 27962306a36Sopenharmony_ci return -EINVAL; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci if (timeout == 0) 28262306a36Sopenharmony_ci timeout = MAX_SCHEDULE_TIMEOUT; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci mutex_lock(&serdev->write_lock); 28562306a36Sopenharmony_ci do { 28662306a36Sopenharmony_ci reinit_completion(&serdev->write_comp); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci ret = ctrl->ops->write_buf(ctrl, buf, count); 28962306a36Sopenharmony_ci if (ret < 0) 29062306a36Sopenharmony_ci break; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci written += ret; 29362306a36Sopenharmony_ci buf += ret; 29462306a36Sopenharmony_ci count -= ret; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci if (count == 0) 29762306a36Sopenharmony_ci break; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci timeout = wait_for_completion_interruptible_timeout(&serdev->write_comp, 30062306a36Sopenharmony_ci timeout); 30162306a36Sopenharmony_ci } while (timeout > 0); 30262306a36Sopenharmony_ci mutex_unlock(&serdev->write_lock); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci if (ret < 0) 30562306a36Sopenharmony_ci return ret; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci if (timeout <= 0 && written == 0) { 30862306a36Sopenharmony_ci if (timeout == -ERESTARTSYS) 30962306a36Sopenharmony_ci return -ERESTARTSYS; 31062306a36Sopenharmony_ci else 31162306a36Sopenharmony_ci return -ETIMEDOUT; 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci return written; 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(serdev_device_write); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_civoid serdev_device_write_flush(struct serdev_device *serdev) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci struct serdev_controller *ctrl = serdev->ctrl; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci if (!ctrl || !ctrl->ops->write_flush) 32362306a36Sopenharmony_ci return; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci ctrl->ops->write_flush(ctrl); 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(serdev_device_write_flush); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ciint serdev_device_write_room(struct serdev_device *serdev) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci struct serdev_controller *ctrl = serdev->ctrl; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci if (!ctrl || !ctrl->ops->write_room) 33462306a36Sopenharmony_ci return 0; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci return serdev->ctrl->ops->write_room(ctrl); 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(serdev_device_write_room); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ciunsigned int serdev_device_set_baudrate(struct serdev_device *serdev, unsigned int speed) 34162306a36Sopenharmony_ci{ 34262306a36Sopenharmony_ci struct serdev_controller *ctrl = serdev->ctrl; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci if (!ctrl || !ctrl->ops->set_baudrate) 34562306a36Sopenharmony_ci return 0; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci return ctrl->ops->set_baudrate(ctrl, speed); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(serdev_device_set_baudrate); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_civoid serdev_device_set_flow_control(struct serdev_device *serdev, bool enable) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci struct serdev_controller *ctrl = serdev->ctrl; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci if (!ctrl || !ctrl->ops->set_flow_control) 35762306a36Sopenharmony_ci return; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci ctrl->ops->set_flow_control(ctrl, enable); 36062306a36Sopenharmony_ci} 36162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(serdev_device_set_flow_control); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ciint serdev_device_set_parity(struct serdev_device *serdev, 36462306a36Sopenharmony_ci enum serdev_parity parity) 36562306a36Sopenharmony_ci{ 36662306a36Sopenharmony_ci struct serdev_controller *ctrl = serdev->ctrl; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci if (!ctrl || !ctrl->ops->set_parity) 36962306a36Sopenharmony_ci return -EOPNOTSUPP; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci return ctrl->ops->set_parity(ctrl, parity); 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(serdev_device_set_parity); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_civoid serdev_device_wait_until_sent(struct serdev_device *serdev, long timeout) 37662306a36Sopenharmony_ci{ 37762306a36Sopenharmony_ci struct serdev_controller *ctrl = serdev->ctrl; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci if (!ctrl || !ctrl->ops->wait_until_sent) 38062306a36Sopenharmony_ci return; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci ctrl->ops->wait_until_sent(ctrl, timeout); 38362306a36Sopenharmony_ci} 38462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(serdev_device_wait_until_sent); 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ciint serdev_device_get_tiocm(struct serdev_device *serdev) 38762306a36Sopenharmony_ci{ 38862306a36Sopenharmony_ci struct serdev_controller *ctrl = serdev->ctrl; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci if (!ctrl || !ctrl->ops->get_tiocm) 39162306a36Sopenharmony_ci return -EOPNOTSUPP; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci return ctrl->ops->get_tiocm(ctrl); 39462306a36Sopenharmony_ci} 39562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(serdev_device_get_tiocm); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ciint serdev_device_set_tiocm(struct serdev_device *serdev, int set, int clear) 39862306a36Sopenharmony_ci{ 39962306a36Sopenharmony_ci struct serdev_controller *ctrl = serdev->ctrl; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci if (!ctrl || !ctrl->ops->set_tiocm) 40262306a36Sopenharmony_ci return -EOPNOTSUPP; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci return ctrl->ops->set_tiocm(ctrl, set, clear); 40562306a36Sopenharmony_ci} 40662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(serdev_device_set_tiocm); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ciint serdev_device_break_ctl(struct serdev_device *serdev, int break_state) 40962306a36Sopenharmony_ci{ 41062306a36Sopenharmony_ci struct serdev_controller *ctrl = serdev->ctrl; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci if (!ctrl || !ctrl->ops->break_ctl) 41362306a36Sopenharmony_ci return -EOPNOTSUPP; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci return ctrl->ops->break_ctl(ctrl, break_state); 41662306a36Sopenharmony_ci} 41762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(serdev_device_break_ctl); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_cistatic int serdev_drv_probe(struct device *dev) 42062306a36Sopenharmony_ci{ 42162306a36Sopenharmony_ci const struct serdev_device_driver *sdrv = to_serdev_device_driver(dev->driver); 42262306a36Sopenharmony_ci int ret; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci ret = dev_pm_domain_attach(dev, true); 42562306a36Sopenharmony_ci if (ret) 42662306a36Sopenharmony_ci return ret; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci ret = sdrv->probe(to_serdev_device(dev)); 42962306a36Sopenharmony_ci if (ret) 43062306a36Sopenharmony_ci dev_pm_domain_detach(dev, true); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci return ret; 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_cistatic void serdev_drv_remove(struct device *dev) 43662306a36Sopenharmony_ci{ 43762306a36Sopenharmony_ci const struct serdev_device_driver *sdrv = to_serdev_device_driver(dev->driver); 43862306a36Sopenharmony_ci if (sdrv->remove) 43962306a36Sopenharmony_ci sdrv->remove(to_serdev_device(dev)); 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci dev_pm_domain_detach(dev, true); 44262306a36Sopenharmony_ci} 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_cistatic struct bus_type serdev_bus_type = { 44562306a36Sopenharmony_ci .name = "serial", 44662306a36Sopenharmony_ci .match = serdev_device_match, 44762306a36Sopenharmony_ci .probe = serdev_drv_probe, 44862306a36Sopenharmony_ci .remove = serdev_drv_remove, 44962306a36Sopenharmony_ci}; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci/** 45262306a36Sopenharmony_ci * serdev_device_alloc() - Allocate a new serdev device 45362306a36Sopenharmony_ci * @ctrl: associated controller 45462306a36Sopenharmony_ci * 45562306a36Sopenharmony_ci * Caller is responsible for either calling serdev_device_add() to add the 45662306a36Sopenharmony_ci * newly allocated controller, or calling serdev_device_put() to discard it. 45762306a36Sopenharmony_ci */ 45862306a36Sopenharmony_cistruct serdev_device *serdev_device_alloc(struct serdev_controller *ctrl) 45962306a36Sopenharmony_ci{ 46062306a36Sopenharmony_ci struct serdev_device *serdev; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci serdev = kzalloc(sizeof(*serdev), GFP_KERNEL); 46362306a36Sopenharmony_ci if (!serdev) 46462306a36Sopenharmony_ci return NULL; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci serdev->ctrl = ctrl; 46762306a36Sopenharmony_ci device_initialize(&serdev->dev); 46862306a36Sopenharmony_ci serdev->dev.parent = &ctrl->dev; 46962306a36Sopenharmony_ci serdev->dev.bus = &serdev_bus_type; 47062306a36Sopenharmony_ci serdev->dev.type = &serdev_device_type; 47162306a36Sopenharmony_ci init_completion(&serdev->write_comp); 47262306a36Sopenharmony_ci mutex_init(&serdev->write_lock); 47362306a36Sopenharmony_ci return serdev; 47462306a36Sopenharmony_ci} 47562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(serdev_device_alloc); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci/** 47862306a36Sopenharmony_ci * serdev_controller_alloc() - Allocate a new serdev controller 47962306a36Sopenharmony_ci * @parent: parent device 48062306a36Sopenharmony_ci * @size: size of private data 48162306a36Sopenharmony_ci * 48262306a36Sopenharmony_ci * Caller is responsible for either calling serdev_controller_add() to add the 48362306a36Sopenharmony_ci * newly allocated controller, or calling serdev_controller_put() to discard it. 48462306a36Sopenharmony_ci * The allocated private data region may be accessed via 48562306a36Sopenharmony_ci * serdev_controller_get_drvdata() 48662306a36Sopenharmony_ci */ 48762306a36Sopenharmony_cistruct serdev_controller *serdev_controller_alloc(struct device *parent, 48862306a36Sopenharmony_ci size_t size) 48962306a36Sopenharmony_ci{ 49062306a36Sopenharmony_ci struct serdev_controller *ctrl; 49162306a36Sopenharmony_ci int id; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci if (WARN_ON(!parent)) 49462306a36Sopenharmony_ci return NULL; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci ctrl = kzalloc(sizeof(*ctrl) + size, GFP_KERNEL); 49762306a36Sopenharmony_ci if (!ctrl) 49862306a36Sopenharmony_ci return NULL; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci id = ida_simple_get(&ctrl_ida, 0, 0, GFP_KERNEL); 50162306a36Sopenharmony_ci if (id < 0) { 50262306a36Sopenharmony_ci dev_err(parent, 50362306a36Sopenharmony_ci "unable to allocate serdev controller identifier.\n"); 50462306a36Sopenharmony_ci goto err_free; 50562306a36Sopenharmony_ci } 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci ctrl->nr = id; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci device_initialize(&ctrl->dev); 51062306a36Sopenharmony_ci ctrl->dev.type = &serdev_ctrl_type; 51162306a36Sopenharmony_ci ctrl->dev.bus = &serdev_bus_type; 51262306a36Sopenharmony_ci ctrl->dev.parent = parent; 51362306a36Sopenharmony_ci ctrl->dev.of_node = parent->of_node; 51462306a36Sopenharmony_ci serdev_controller_set_drvdata(ctrl, &ctrl[1]); 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci dev_set_name(&ctrl->dev, "serial%d", id); 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci pm_runtime_no_callbacks(&ctrl->dev); 51962306a36Sopenharmony_ci pm_suspend_ignore_children(&ctrl->dev, true); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci dev_dbg(&ctrl->dev, "allocated controller 0x%p id %d\n", ctrl, id); 52262306a36Sopenharmony_ci return ctrl; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_cierr_free: 52562306a36Sopenharmony_ci kfree(ctrl); 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci return NULL; 52862306a36Sopenharmony_ci} 52962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(serdev_controller_alloc); 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_cistatic int of_serdev_register_devices(struct serdev_controller *ctrl) 53262306a36Sopenharmony_ci{ 53362306a36Sopenharmony_ci struct device_node *node; 53462306a36Sopenharmony_ci struct serdev_device *serdev = NULL; 53562306a36Sopenharmony_ci int err; 53662306a36Sopenharmony_ci bool found = false; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci for_each_available_child_of_node(ctrl->dev.of_node, node) { 53962306a36Sopenharmony_ci if (!of_get_property(node, "compatible", NULL)) 54062306a36Sopenharmony_ci continue; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci dev_dbg(&ctrl->dev, "adding child %pOF\n", node); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci serdev = serdev_device_alloc(ctrl); 54562306a36Sopenharmony_ci if (!serdev) 54662306a36Sopenharmony_ci continue; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci device_set_node(&serdev->dev, of_fwnode_handle(node)); 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci err = serdev_device_add(serdev); 55162306a36Sopenharmony_ci if (err) { 55262306a36Sopenharmony_ci dev_err(&serdev->dev, 55362306a36Sopenharmony_ci "failure adding device. status %pe\n", 55462306a36Sopenharmony_ci ERR_PTR(err)); 55562306a36Sopenharmony_ci serdev_device_put(serdev); 55662306a36Sopenharmony_ci } else 55762306a36Sopenharmony_ci found = true; 55862306a36Sopenharmony_ci } 55962306a36Sopenharmony_ci if (!found) 56062306a36Sopenharmony_ci return -ENODEV; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci return 0; 56362306a36Sopenharmony_ci} 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci#ifdef CONFIG_ACPI 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci#define SERDEV_ACPI_MAX_SCAN_DEPTH 32 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_cistruct acpi_serdev_lookup { 57062306a36Sopenharmony_ci acpi_handle device_handle; 57162306a36Sopenharmony_ci acpi_handle controller_handle; 57262306a36Sopenharmony_ci int n; 57362306a36Sopenharmony_ci int index; 57462306a36Sopenharmony_ci}; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci/** 57762306a36Sopenharmony_ci * serdev_acpi_get_uart_resource - Gets UARTSerialBus resource if type matches 57862306a36Sopenharmony_ci * @ares: ACPI resource 57962306a36Sopenharmony_ci * @uart: Pointer to UARTSerialBus resource will be returned here 58062306a36Sopenharmony_ci * 58162306a36Sopenharmony_ci * Checks if the given ACPI resource is of type UARTSerialBus. 58262306a36Sopenharmony_ci * In this case, returns a pointer to it to the caller. 58362306a36Sopenharmony_ci * 58462306a36Sopenharmony_ci * Return: True if resource type is of UARTSerialBus, otherwise false. 58562306a36Sopenharmony_ci */ 58662306a36Sopenharmony_cibool serdev_acpi_get_uart_resource(struct acpi_resource *ares, 58762306a36Sopenharmony_ci struct acpi_resource_uart_serialbus **uart) 58862306a36Sopenharmony_ci{ 58962306a36Sopenharmony_ci struct acpi_resource_uart_serialbus *sb; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci if (ares->type != ACPI_RESOURCE_TYPE_SERIAL_BUS) 59262306a36Sopenharmony_ci return false; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci sb = &ares->data.uart_serial_bus; 59562306a36Sopenharmony_ci if (sb->type != ACPI_RESOURCE_SERIAL_TYPE_UART) 59662306a36Sopenharmony_ci return false; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci *uart = sb; 59962306a36Sopenharmony_ci return true; 60062306a36Sopenharmony_ci} 60162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(serdev_acpi_get_uart_resource); 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_cistatic int acpi_serdev_parse_resource(struct acpi_resource *ares, void *data) 60462306a36Sopenharmony_ci{ 60562306a36Sopenharmony_ci struct acpi_serdev_lookup *lookup = data; 60662306a36Sopenharmony_ci struct acpi_resource_uart_serialbus *sb; 60762306a36Sopenharmony_ci acpi_status status; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci if (!serdev_acpi_get_uart_resource(ares, &sb)) 61062306a36Sopenharmony_ci return 1; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci if (lookup->index != -1 && lookup->n++ != lookup->index) 61362306a36Sopenharmony_ci return 1; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci status = acpi_get_handle(lookup->device_handle, 61662306a36Sopenharmony_ci sb->resource_source.string_ptr, 61762306a36Sopenharmony_ci &lookup->controller_handle); 61862306a36Sopenharmony_ci if (ACPI_FAILURE(status)) 61962306a36Sopenharmony_ci return 1; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci /* 62262306a36Sopenharmony_ci * NOTE: Ideally, we would also want to retrieve other properties here, 62362306a36Sopenharmony_ci * once setting them before opening the device is supported by serdev. 62462306a36Sopenharmony_ci */ 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci return 1; 62762306a36Sopenharmony_ci} 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_cistatic int acpi_serdev_do_lookup(struct acpi_device *adev, 63062306a36Sopenharmony_ci struct acpi_serdev_lookup *lookup) 63162306a36Sopenharmony_ci{ 63262306a36Sopenharmony_ci struct list_head resource_list; 63362306a36Sopenharmony_ci int ret; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci lookup->device_handle = acpi_device_handle(adev); 63662306a36Sopenharmony_ci lookup->controller_handle = NULL; 63762306a36Sopenharmony_ci lookup->n = 0; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci INIT_LIST_HEAD(&resource_list); 64062306a36Sopenharmony_ci ret = acpi_dev_get_resources(adev, &resource_list, 64162306a36Sopenharmony_ci acpi_serdev_parse_resource, lookup); 64262306a36Sopenharmony_ci acpi_dev_free_resource_list(&resource_list); 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci if (ret < 0) 64562306a36Sopenharmony_ci return -EINVAL; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci return 0; 64862306a36Sopenharmony_ci} 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_cistatic int acpi_serdev_check_resources(struct serdev_controller *ctrl, 65162306a36Sopenharmony_ci struct acpi_device *adev) 65262306a36Sopenharmony_ci{ 65362306a36Sopenharmony_ci struct acpi_serdev_lookup lookup; 65462306a36Sopenharmony_ci int ret; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci if (acpi_bus_get_status(adev) || !adev->status.present) 65762306a36Sopenharmony_ci return -EINVAL; 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci /* Look for UARTSerialBusV2 resource */ 66062306a36Sopenharmony_ci lookup.index = -1; // we only care for the last device 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci ret = acpi_serdev_do_lookup(adev, &lookup); 66362306a36Sopenharmony_ci if (ret) 66462306a36Sopenharmony_ci return ret; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci /* 66762306a36Sopenharmony_ci * Apple machines provide an empty resource template, so on those 66862306a36Sopenharmony_ci * machines just look for immediate children with a "baud" property 66962306a36Sopenharmony_ci * (from the _DSM method) instead. 67062306a36Sopenharmony_ci */ 67162306a36Sopenharmony_ci if (!lookup.controller_handle && x86_apple_machine && 67262306a36Sopenharmony_ci !acpi_dev_get_property(adev, "baud", ACPI_TYPE_BUFFER, NULL)) 67362306a36Sopenharmony_ci acpi_get_parent(adev->handle, &lookup.controller_handle); 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci /* Make sure controller and ResourceSource handle match */ 67662306a36Sopenharmony_ci if (ACPI_HANDLE(ctrl->dev.parent) != lookup.controller_handle) 67762306a36Sopenharmony_ci return -ENODEV; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci return 0; 68062306a36Sopenharmony_ci} 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_cistatic acpi_status acpi_serdev_register_device(struct serdev_controller *ctrl, 68362306a36Sopenharmony_ci struct acpi_device *adev) 68462306a36Sopenharmony_ci{ 68562306a36Sopenharmony_ci struct serdev_device *serdev; 68662306a36Sopenharmony_ci int err; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci serdev = serdev_device_alloc(ctrl); 68962306a36Sopenharmony_ci if (!serdev) { 69062306a36Sopenharmony_ci dev_err(&ctrl->dev, "failed to allocate serdev device for %s\n", 69162306a36Sopenharmony_ci dev_name(&adev->dev)); 69262306a36Sopenharmony_ci return AE_NO_MEMORY; 69362306a36Sopenharmony_ci } 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci ACPI_COMPANION_SET(&serdev->dev, adev); 69662306a36Sopenharmony_ci acpi_device_set_enumerated(adev); 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci err = serdev_device_add(serdev); 69962306a36Sopenharmony_ci if (err) { 70062306a36Sopenharmony_ci dev_err(&serdev->dev, 70162306a36Sopenharmony_ci "failure adding ACPI serdev device. status %pe\n", 70262306a36Sopenharmony_ci ERR_PTR(err)); 70362306a36Sopenharmony_ci serdev_device_put(serdev); 70462306a36Sopenharmony_ci } 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci return AE_OK; 70762306a36Sopenharmony_ci} 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_cistatic const struct acpi_device_id serdev_acpi_devices_blacklist[] = { 71062306a36Sopenharmony_ci { "INT3511", 0 }, 71162306a36Sopenharmony_ci { "INT3512", 0 }, 71262306a36Sopenharmony_ci { }, 71362306a36Sopenharmony_ci}; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_cistatic acpi_status acpi_serdev_add_device(acpi_handle handle, u32 level, 71662306a36Sopenharmony_ci void *data, void **return_value) 71762306a36Sopenharmony_ci{ 71862306a36Sopenharmony_ci struct acpi_device *adev = acpi_fetch_acpi_dev(handle); 71962306a36Sopenharmony_ci struct serdev_controller *ctrl = data; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci if (!adev || acpi_device_enumerated(adev)) 72262306a36Sopenharmony_ci return AE_OK; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci /* Skip if black listed */ 72562306a36Sopenharmony_ci if (!acpi_match_device_ids(adev, serdev_acpi_devices_blacklist)) 72662306a36Sopenharmony_ci return AE_OK; 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci if (acpi_serdev_check_resources(ctrl, adev)) 72962306a36Sopenharmony_ci return AE_OK; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci return acpi_serdev_register_device(ctrl, adev); 73262306a36Sopenharmony_ci} 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_cistatic int acpi_serdev_register_devices(struct serdev_controller *ctrl) 73662306a36Sopenharmony_ci{ 73762306a36Sopenharmony_ci acpi_status status; 73862306a36Sopenharmony_ci bool skip; 73962306a36Sopenharmony_ci int ret; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci if (!has_acpi_companion(ctrl->dev.parent)) 74262306a36Sopenharmony_ci return -ENODEV; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci /* 74562306a36Sopenharmony_ci * Skip registration on boards where the ACPI tables are known to 74662306a36Sopenharmony_ci * contain buggy devices. Note serdev_controller_add() must still 74762306a36Sopenharmony_ci * succeed in this case, so that the proper serdev devices can be 74862306a36Sopenharmony_ci * added "manually" later. 74962306a36Sopenharmony_ci */ 75062306a36Sopenharmony_ci ret = acpi_quirk_skip_serdev_enumeration(ctrl->dev.parent, &skip); 75162306a36Sopenharmony_ci if (ret) 75262306a36Sopenharmony_ci return ret; 75362306a36Sopenharmony_ci if (skip) 75462306a36Sopenharmony_ci return 0; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, 75762306a36Sopenharmony_ci SERDEV_ACPI_MAX_SCAN_DEPTH, 75862306a36Sopenharmony_ci acpi_serdev_add_device, NULL, ctrl, NULL); 75962306a36Sopenharmony_ci if (ACPI_FAILURE(status)) 76062306a36Sopenharmony_ci dev_warn(&ctrl->dev, "failed to enumerate serdev slaves\n"); 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci if (!ctrl->serdev) 76362306a36Sopenharmony_ci return -ENODEV; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci return 0; 76662306a36Sopenharmony_ci} 76762306a36Sopenharmony_ci#else 76862306a36Sopenharmony_cistatic inline int acpi_serdev_register_devices(struct serdev_controller *ctrl) 76962306a36Sopenharmony_ci{ 77062306a36Sopenharmony_ci return -ENODEV; 77162306a36Sopenharmony_ci} 77262306a36Sopenharmony_ci#endif /* CONFIG_ACPI */ 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci/** 77562306a36Sopenharmony_ci * serdev_controller_add() - Add an serdev controller 77662306a36Sopenharmony_ci * @ctrl: controller to be registered. 77762306a36Sopenharmony_ci * 77862306a36Sopenharmony_ci * Register a controller previously allocated via serdev_controller_alloc() with 77962306a36Sopenharmony_ci * the serdev core. 78062306a36Sopenharmony_ci */ 78162306a36Sopenharmony_ciint serdev_controller_add(struct serdev_controller *ctrl) 78262306a36Sopenharmony_ci{ 78362306a36Sopenharmony_ci int ret_of, ret_acpi, ret; 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci /* Can't register until after driver model init */ 78662306a36Sopenharmony_ci if (WARN_ON(!is_registered)) 78762306a36Sopenharmony_ci return -EAGAIN; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci ret = device_add(&ctrl->dev); 79062306a36Sopenharmony_ci if (ret) 79162306a36Sopenharmony_ci return ret; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci pm_runtime_enable(&ctrl->dev); 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci ret_of = of_serdev_register_devices(ctrl); 79662306a36Sopenharmony_ci ret_acpi = acpi_serdev_register_devices(ctrl); 79762306a36Sopenharmony_ci if (ret_of && ret_acpi) { 79862306a36Sopenharmony_ci dev_dbg(&ctrl->dev, "no devices registered: of:%pe acpi:%pe\n", 79962306a36Sopenharmony_ci ERR_PTR(ret_of), ERR_PTR(ret_acpi)); 80062306a36Sopenharmony_ci ret = -ENODEV; 80162306a36Sopenharmony_ci goto err_rpm_disable; 80262306a36Sopenharmony_ci } 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci dev_dbg(&ctrl->dev, "serdev%d registered: dev:%p\n", 80562306a36Sopenharmony_ci ctrl->nr, &ctrl->dev); 80662306a36Sopenharmony_ci return 0; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_cierr_rpm_disable: 80962306a36Sopenharmony_ci pm_runtime_disable(&ctrl->dev); 81062306a36Sopenharmony_ci device_del(&ctrl->dev); 81162306a36Sopenharmony_ci return ret; 81262306a36Sopenharmony_ci}; 81362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(serdev_controller_add); 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci/* Remove a device associated with a controller */ 81662306a36Sopenharmony_cistatic int serdev_remove_device(struct device *dev, void *data) 81762306a36Sopenharmony_ci{ 81862306a36Sopenharmony_ci struct serdev_device *serdev = to_serdev_device(dev); 81962306a36Sopenharmony_ci if (dev->type == &serdev_device_type) 82062306a36Sopenharmony_ci serdev_device_remove(serdev); 82162306a36Sopenharmony_ci return 0; 82262306a36Sopenharmony_ci} 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci/** 82562306a36Sopenharmony_ci * serdev_controller_remove(): remove an serdev controller 82662306a36Sopenharmony_ci * @ctrl: controller to remove 82762306a36Sopenharmony_ci * 82862306a36Sopenharmony_ci * Remove a serdev controller. Caller is responsible for calling 82962306a36Sopenharmony_ci * serdev_controller_put() to discard the allocated controller. 83062306a36Sopenharmony_ci */ 83162306a36Sopenharmony_civoid serdev_controller_remove(struct serdev_controller *ctrl) 83262306a36Sopenharmony_ci{ 83362306a36Sopenharmony_ci if (!ctrl) 83462306a36Sopenharmony_ci return; 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci device_for_each_child(&ctrl->dev, NULL, serdev_remove_device); 83762306a36Sopenharmony_ci pm_runtime_disable(&ctrl->dev); 83862306a36Sopenharmony_ci device_del(&ctrl->dev); 83962306a36Sopenharmony_ci} 84062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(serdev_controller_remove); 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci/** 84362306a36Sopenharmony_ci * __serdev_device_driver_register() - Register client driver with serdev core 84462306a36Sopenharmony_ci * @sdrv: client driver to be associated with client-device. 84562306a36Sopenharmony_ci * @owner: client driver owner to set. 84662306a36Sopenharmony_ci * 84762306a36Sopenharmony_ci * This API will register the client driver with the serdev framework. 84862306a36Sopenharmony_ci * It is typically called from the driver's module-init function. 84962306a36Sopenharmony_ci */ 85062306a36Sopenharmony_ciint __serdev_device_driver_register(struct serdev_device_driver *sdrv, struct module *owner) 85162306a36Sopenharmony_ci{ 85262306a36Sopenharmony_ci sdrv->driver.bus = &serdev_bus_type; 85362306a36Sopenharmony_ci sdrv->driver.owner = owner; 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci /* force drivers to async probe so I/O is possible in probe */ 85662306a36Sopenharmony_ci sdrv->driver.probe_type = PROBE_PREFER_ASYNCHRONOUS; 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci return driver_register(&sdrv->driver); 85962306a36Sopenharmony_ci} 86062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__serdev_device_driver_register); 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_cistatic void __exit serdev_exit(void) 86362306a36Sopenharmony_ci{ 86462306a36Sopenharmony_ci bus_unregister(&serdev_bus_type); 86562306a36Sopenharmony_ci ida_destroy(&ctrl_ida); 86662306a36Sopenharmony_ci} 86762306a36Sopenharmony_cimodule_exit(serdev_exit); 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_cistatic int __init serdev_init(void) 87062306a36Sopenharmony_ci{ 87162306a36Sopenharmony_ci int ret; 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci ret = bus_register(&serdev_bus_type); 87462306a36Sopenharmony_ci if (ret) 87562306a36Sopenharmony_ci return ret; 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci is_registered = true; 87862306a36Sopenharmony_ci return 0; 87962306a36Sopenharmony_ci} 88062306a36Sopenharmony_ci/* Must be before serial drivers register */ 88162306a36Sopenharmony_cipostcore_initcall(serdev_init); 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ciMODULE_AUTHOR("Rob Herring <robh@kernel.org>"); 88462306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 88562306a36Sopenharmony_ciMODULE_DESCRIPTION("Serial attached device bus"); 886