mirror of
https://github.com/torvalds/linux.git
synced 2026-07-31 11:37:06 +02:00
ucsi: ucsi_glink: Notify upon change in connector status
Add APIs to allow clients to register for (and unregister from) receiving notifications whenever a response is received for a GET_CONNECTOR_STATUS request. Currently the notifications contain only a single field indicating whether or not a Debug Accessory or an Audio Adapter Accessory is attached (parsed from the received response). Change-Id: If1ef46f5f9e4b4fa93f32bfd06fd50c7e16f7874 Signed-off-by: Guru Das Srinagesh <gurus@codeaurora.org>
This commit is contained in:
parent
eba564a773
commit
37e61f7dbc
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2019, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2019-2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "UCSI: %s: " fmt, __func__
|
||||
|
|
@ -8,9 +8,12 @@
|
|||
#include <linux/device.h>
|
||||
#include <linux/ipc_logging.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/soc/qcom/pmic_glink.h>
|
||||
#include <linux/usb/typec.h>
|
||||
#include <linux/usb/ucsi_glink.h>
|
||||
|
||||
#include "ucsi.h"
|
||||
|
||||
|
|
@ -74,12 +77,29 @@ struct ucsi_dev {
|
|||
struct completion sync_write_ack;
|
||||
struct mutex read_lock;
|
||||
struct mutex write_lock;
|
||||
struct mutex notify_lock;
|
||||
struct ucsi_read_buf_resp_msg rx_buf;
|
||||
unsigned long flags;
|
||||
atomic_t rx_valid;
|
||||
unsigned long cmd_requested_flags;
|
||||
struct ucsi_glink_constat_info constat_info;
|
||||
struct work_struct notify_work;
|
||||
};
|
||||
|
||||
static void *ucsi_ipc_log;
|
||||
static RAW_NOTIFIER_HEAD(ucsi_glink_notifier);
|
||||
|
||||
int register_ucsi_glink_notifier(struct notifier_block *nb)
|
||||
{
|
||||
return raw_notifier_chain_register(&ucsi_glink_notifier, nb);
|
||||
}
|
||||
EXPORT_SYMBOL(register_ucsi_glink_notifier);
|
||||
|
||||
int unregister_ucsi_glink_notifier(struct notifier_block *nb)
|
||||
{
|
||||
return raw_notifier_chain_unregister(&ucsi_glink_notifier, nb);
|
||||
}
|
||||
EXPORT_SYMBOL(unregister_ucsi_glink_notifier);
|
||||
|
||||
static char *offset_to_name(unsigned int offset)
|
||||
{
|
||||
|
|
@ -237,6 +257,7 @@ static bool validate_ucsi_msg(unsigned int offset, size_t len)
|
|||
return true;
|
||||
}
|
||||
|
||||
#define CONN_STAT_REQD 1
|
||||
static int ucsi_qti_glink_write(struct ucsi_dev *udev, unsigned int offset,
|
||||
const void *val, size_t val_len, bool sync)
|
||||
{
|
||||
|
|
@ -291,6 +312,13 @@ static int ucsi_qti_glink_write(struct ucsi_dev *udev, unsigned int offset,
|
|||
|
||||
ucsi_log(sync ? "sync_write:" : "async_write:", offset,
|
||||
(u8 *)val, val_len);
|
||||
|
||||
if (((u8 *)val)[0] == UCSI_GET_CONNECTOR_STATUS) {
|
||||
mutex_lock(&udev->notify_lock);
|
||||
set_bit(CONN_STAT_REQD, &udev->cmd_requested_flags);
|
||||
mutex_unlock(&udev->notify_lock);
|
||||
}
|
||||
|
||||
out:
|
||||
if (sync)
|
||||
clear_bit(CMD_PENDING, &udev->flags);
|
||||
|
|
@ -316,6 +344,47 @@ static int ucsi_qti_sync_write(struct ucsi *ucsi, unsigned int offset,
|
|||
return ucsi_qti_glink_write(udev, offset, val, val_len, true);
|
||||
}
|
||||
|
||||
static void ucsi_qti_notify_work(struct work_struct *work)
|
||||
{
|
||||
struct ucsi_dev *udev = container_of(work, struct ucsi_dev,
|
||||
notify_work);
|
||||
|
||||
raw_notifier_call_chain(&ucsi_glink_notifier, 0, &udev->constat_info);
|
||||
mutex_lock(&udev->notify_lock);
|
||||
clear_bit(CONN_STAT_REQD, &udev->cmd_requested_flags);
|
||||
mutex_unlock(&udev->notify_lock);
|
||||
}
|
||||
|
||||
static void ucsi_qti_notify(struct ucsi_dev *udev, unsigned int offset,
|
||||
struct ucsi_connector_status *status)
|
||||
{
|
||||
u8 conn_partner_type;
|
||||
bool cmd_requested;
|
||||
|
||||
mutex_lock(&udev->notify_lock);
|
||||
cmd_requested = test_bit(CONN_STAT_REQD, &udev->cmd_requested_flags);
|
||||
mutex_unlock(&udev->notify_lock);
|
||||
|
||||
if (cmd_requested && offset == UCSI_MESSAGE_IN) {
|
||||
cancel_work_sync(&udev->notify_work);
|
||||
conn_partner_type = UCSI_CONSTAT_PARTNER_TYPE(status->flags);
|
||||
|
||||
switch (conn_partner_type) {
|
||||
case UCSI_CONSTAT_PARTNER_TYPE_AUDIO:
|
||||
udev->constat_info.acc = TYPEC_ACCESSORY_AUDIO;
|
||||
break;
|
||||
case UCSI_CONSTAT_PARTNER_TYPE_DEBUG:
|
||||
udev->constat_info.acc = TYPEC_ACCESSORY_DEBUG;
|
||||
break;
|
||||
default:
|
||||
udev->constat_info.acc = TYPEC_ACCESSORY_NONE;
|
||||
break;
|
||||
}
|
||||
|
||||
schedule_work(&udev->notify_work);
|
||||
}
|
||||
}
|
||||
|
||||
static int ucsi_qti_read(struct ucsi *ucsi, unsigned int offset,
|
||||
void *val, size_t val_len)
|
||||
{
|
||||
|
|
@ -359,6 +428,7 @@ static int ucsi_qti_read(struct ucsi *ucsi, unsigned int offset,
|
|||
memcpy((u8 *)val, &udev->rx_buf.buf[offset], val_len);
|
||||
atomic_set(&udev->rx_valid, 0);
|
||||
ucsi_log("read:", offset, (u8 *)val, val_len);
|
||||
ucsi_qti_notify(udev, offset, val);
|
||||
|
||||
out:
|
||||
mutex_unlock(&udev->read_lock);
|
||||
|
|
@ -383,8 +453,10 @@ static int ucsi_probe(struct platform_device *pdev)
|
|||
if (!udev)
|
||||
return -ENOMEM;
|
||||
|
||||
INIT_WORK(&udev->notify_work, ucsi_qti_notify_work);
|
||||
mutex_init(&udev->read_lock);
|
||||
mutex_init(&udev->write_lock);
|
||||
mutex_init(&udev->notify_lock);
|
||||
init_completion(&udev->read_ack);
|
||||
init_completion(&udev->write_ack);
|
||||
init_completion(&udev->sync_write_ack);
|
||||
|
|
@ -442,6 +514,7 @@ static int ucsi_remove(struct platform_device *pdev)
|
|||
struct ucsi_dev *udev = dev_get_drvdata(dev);
|
||||
int rc;
|
||||
|
||||
cancel_work_sync(&udev->notify_work);
|
||||
ucsi_unregister(udev->ucsi);
|
||||
ucsi_destroy(udev->ucsi);
|
||||
|
||||
|
|
|
|||
36
include/linux/usb/ucsi_glink.h
Normal file
36
include/linux/usb/ucsi_glink.h
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __UCSI_GLINK_H__
|
||||
#define __UCSI_GLINK_H__
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/usb/typec.h>
|
||||
|
||||
struct ucsi_glink_constat_info {
|
||||
enum typec_accessory acc;
|
||||
};
|
||||
|
||||
struct notifier_block;
|
||||
|
||||
#if IS_ENABLED(CONFIG_UCSI_QTI_GLINK)
|
||||
|
||||
int register_ucsi_glink_notifier(struct notifier_block *nb);
|
||||
int unregister_ucsi_glink_notifier(struct notifier_block *nb);
|
||||
|
||||
#else
|
||||
|
||||
static inline int register_ucsi_glink_notifier(struct notifier_block *nb)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline int unregister_ucsi_glink_notifier(struct notifier_block *nb)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
Loading…
Reference in New Issue
Block a user