mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-08 12:58:12 -04:00
Merge "usb: typec: ucsi: Optimise ucsi_unregister()"
This commit is contained in:
commit
5ec3e080e9
7 changed files with 637 additions and 754 deletions
|
|
@ -48,7 +48,8 @@ struct ucsi_dp {
|
|||
static int ucsi_displayport_enter(struct typec_altmode *alt)
|
||||
{
|
||||
struct ucsi_dp *dp = typec_altmode_get_drvdata(alt);
|
||||
struct ucsi_control ctrl;
|
||||
struct ucsi *ucsi = dp->con->ucsi;
|
||||
u64 command;
|
||||
u8 cur = 0;
|
||||
int ret;
|
||||
|
||||
|
|
@ -59,25 +60,21 @@ static int ucsi_displayport_enter(struct typec_altmode *alt)
|
|||
|
||||
dev_warn(&p->dev,
|
||||
"firmware doesn't support alternate mode overriding\n");
|
||||
mutex_unlock(&dp->con->lock);
|
||||
return -EOPNOTSUPP;
|
||||
ret = -EOPNOTSUPP;
|
||||
goto err_unlock;
|
||||
}
|
||||
|
||||
UCSI_CMD_GET_CURRENT_CAM(ctrl, dp->con->num);
|
||||
ret = ucsi_send_command(dp->con->ucsi, &ctrl, &cur, sizeof(cur));
|
||||
command = UCSI_GET_CURRENT_CAM | UCSI_CONNECTOR_NUMBER(dp->con->num);
|
||||
ret = ucsi_send_command(ucsi, command, &cur, sizeof(cur));
|
||||
if (ret < 0) {
|
||||
if (dp->con->ucsi->ppm->data->version > 0x0100) {
|
||||
mutex_unlock(&dp->con->lock);
|
||||
return ret;
|
||||
}
|
||||
if (ucsi->version > 0x0100)
|
||||
goto err_unlock;
|
||||
cur = 0xff;
|
||||
}
|
||||
|
||||
if (cur != 0xff) {
|
||||
mutex_unlock(&dp->con->lock);
|
||||
if (dp->con->port_altmode[cur] == alt)
|
||||
return 0;
|
||||
return -EBUSY;
|
||||
ret = dp->con->port_altmode[cur] == alt ? 0 : -EBUSY;
|
||||
goto err_unlock;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -94,16 +91,17 @@ static int ucsi_displayport_enter(struct typec_altmode *alt)
|
|||
dp->vdo_size = 1;
|
||||
|
||||
schedule_work(&dp->work);
|
||||
|
||||
ret = 0;
|
||||
err_unlock:
|
||||
mutex_unlock(&dp->con->lock);
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ucsi_displayport_exit(struct typec_altmode *alt)
|
||||
{
|
||||
struct ucsi_dp *dp = typec_altmode_get_drvdata(alt);
|
||||
struct ucsi_control ctrl;
|
||||
u64 command;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&dp->con->lock);
|
||||
|
|
@ -117,8 +115,8 @@ static int ucsi_displayport_exit(struct typec_altmode *alt)
|
|||
goto out_unlock;
|
||||
}
|
||||
|
||||
ctrl.raw_cmd = UCSI_CMD_SET_NEW_CAM(dp->con->num, 0, dp->offset, 0);
|
||||
ret = ucsi_send_command(dp->con->ucsi, &ctrl, NULL, 0);
|
||||
command = UCSI_CMD_SET_NEW_CAM(dp->con->num, 0, dp->offset, 0);
|
||||
ret = ucsi_send_command(dp->con->ucsi, command, NULL, 0);
|
||||
if (ret < 0)
|
||||
goto out_unlock;
|
||||
|
||||
|
|
@ -172,14 +170,14 @@ static int ucsi_displayport_status_update(struct ucsi_dp *dp)
|
|||
static int ucsi_displayport_configure(struct ucsi_dp *dp)
|
||||
{
|
||||
u32 pins = DP_CONF_GET_PIN_ASSIGN(dp->data.conf);
|
||||
struct ucsi_control ctrl;
|
||||
u64 command;
|
||||
|
||||
if (!dp->override)
|
||||
return 0;
|
||||
|
||||
ctrl.raw_cmd = UCSI_CMD_SET_NEW_CAM(dp->con->num, 1, dp->offset, pins);
|
||||
command = UCSI_CMD_SET_NEW_CAM(dp->con->num, 1, dp->offset, pins);
|
||||
|
||||
return ucsi_send_command(dp->con->ucsi, &ctrl, NULL, 0);
|
||||
return ucsi_send_command(dp->con->ucsi, command, NULL, 0);
|
||||
}
|
||||
|
||||
static int ucsi_displayport_vdm(struct typec_altmode *alt,
|
||||
|
|
|
|||
|
|
@ -33,17 +33,6 @@ const char *ucsi_cmd_str(u64 raw_cmd)
|
|||
return ucsi_cmd_strs[(cmd >= ARRAY_SIZE(ucsi_cmd_strs)) ? 0 : cmd];
|
||||
}
|
||||
|
||||
static const char * const ucsi_ack_strs[] = {
|
||||
[0] = "",
|
||||
[UCSI_ACK_EVENT] = "event",
|
||||
[UCSI_ACK_CMD] = "command",
|
||||
};
|
||||
|
||||
const char *ucsi_ack_str(u8 ack)
|
||||
{
|
||||
return ucsi_ack_strs[(ack >= ARRAY_SIZE(ucsi_ack_strs)) ? 0 : ack];
|
||||
}
|
||||
|
||||
const char *ucsi_cci_str(u32 cci)
|
||||
{
|
||||
if (cci & GENMASK(7, 0)) {
|
||||
|
|
|
|||
|
|
@ -10,54 +10,18 @@
|
|||
#include <linux/usb/typec_altmode.h>
|
||||
|
||||
const char *ucsi_cmd_str(u64 raw_cmd);
|
||||
const char *ucsi_ack_str(u8 ack);
|
||||
const char *ucsi_cci_str(u32 cci);
|
||||
const char *ucsi_recipient_str(u8 recipient);
|
||||
|
||||
DECLARE_EVENT_CLASS(ucsi_log_ack,
|
||||
TP_PROTO(u8 ack),
|
||||
TP_ARGS(ack),
|
||||
TP_STRUCT__entry(
|
||||
__field(u8, ack)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->ack = ack;
|
||||
),
|
||||
TP_printk("ACK %s", ucsi_ack_str(__entry->ack))
|
||||
);
|
||||
|
||||
DEFINE_EVENT(ucsi_log_ack, ucsi_ack,
|
||||
TP_PROTO(u8 ack),
|
||||
TP_ARGS(ack)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(ucsi_log_control,
|
||||
TP_PROTO(struct ucsi_control *ctrl),
|
||||
TP_ARGS(ctrl),
|
||||
TP_STRUCT__entry(
|
||||
__field(u64, ctrl)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->ctrl = ctrl->raw_cmd;
|
||||
),
|
||||
TP_printk("control=%08llx (%s)", __entry->ctrl,
|
||||
ucsi_cmd_str(__entry->ctrl))
|
||||
);
|
||||
|
||||
DEFINE_EVENT(ucsi_log_control, ucsi_command,
|
||||
TP_PROTO(struct ucsi_control *ctrl),
|
||||
TP_ARGS(ctrl)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(ucsi_log_command,
|
||||
TP_PROTO(struct ucsi_control *ctrl, int ret),
|
||||
TP_ARGS(ctrl, ret),
|
||||
TP_PROTO(u64 command, int ret),
|
||||
TP_ARGS(command, ret),
|
||||
TP_STRUCT__entry(
|
||||
__field(u64, ctrl)
|
||||
__field(int, ret)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->ctrl = ctrl->raw_cmd;
|
||||
__entry->ctrl = command;
|
||||
__entry->ret = ret;
|
||||
),
|
||||
TP_printk("%s -> %s (err=%d)", ucsi_cmd_str(__entry->ctrl),
|
||||
|
|
@ -66,30 +30,13 @@ DECLARE_EVENT_CLASS(ucsi_log_command,
|
|||
);
|
||||
|
||||
DEFINE_EVENT(ucsi_log_command, ucsi_run_command,
|
||||
TP_PROTO(struct ucsi_control *ctrl, int ret),
|
||||
TP_ARGS(ctrl, ret)
|
||||
TP_PROTO(u64 command, int ret),
|
||||
TP_ARGS(command, ret)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(ucsi_log_command, ucsi_reset_ppm,
|
||||
TP_PROTO(struct ucsi_control *ctrl, int ret),
|
||||
TP_ARGS(ctrl, ret)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(ucsi_log_cci,
|
||||
TP_PROTO(u32 cci),
|
||||
TP_ARGS(cci),
|
||||
TP_STRUCT__entry(
|
||||
__field(u32, cci)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->cci = cci;
|
||||
),
|
||||
TP_printk("CCI=%08x %s", __entry->cci, ucsi_cci_str(__entry->cci))
|
||||
);
|
||||
|
||||
DEFINE_EVENT(ucsi_log_cci, ucsi_notify,
|
||||
TP_PROTO(u32 cci),
|
||||
TP_ARGS(cci)
|
||||
TP_PROTO(u64 command, int ret),
|
||||
TP_ARGS(command, ret)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(ucsi_log_connector_status,
|
||||
|
|
@ -109,13 +56,13 @@ DECLARE_EVENT_CLASS(ucsi_log_connector_status,
|
|||
TP_fast_assign(
|
||||
__entry->port = port - 1;
|
||||
__entry->change = status->change;
|
||||
__entry->opmode = status->pwr_op_mode;
|
||||
__entry->connected = status->connected;
|
||||
__entry->pwr_dir = status->pwr_dir;
|
||||
__entry->partner_flags = status->partner_flags;
|
||||
__entry->partner_type = status->partner_type;
|
||||
__entry->opmode = UCSI_CONSTAT_PWR_OPMODE(status->flags);
|
||||
__entry->connected = !!(status->flags & UCSI_CONSTAT_CONNECTED);
|
||||
__entry->pwr_dir = !!(status->flags & UCSI_CONSTAT_PWR_DIR);
|
||||
__entry->partner_flags = UCSI_CONSTAT_PARTNER_FLAGS(status->flags);
|
||||
__entry->partner_type = UCSI_CONSTAT_PARTNER_TYPE(status->flags);
|
||||
__entry->request_data_obj = status->request_data_obj;
|
||||
__entry->bc_status = status->bc_status;
|
||||
__entry->bc_status = UCSI_CONSTAT_BC_STATUS(status->pwr_status);
|
||||
),
|
||||
TP_printk("port%d status: change=%04x, opmode=%x, connected=%d, "
|
||||
"sourcing=%d, partner_flags=%x, partner_type=%x, "
|
||||
|
|
|
|||
|
|
@ -36,169 +36,148 @@
|
|||
*/
|
||||
#define UCSI_SWAP_TIMEOUT_MS 5000
|
||||
|
||||
static inline int ucsi_sync(struct ucsi *ucsi)
|
||||
static int ucsi_acknowledge_command(struct ucsi *ucsi)
|
||||
{
|
||||
if (ucsi->ppm && ucsi->ppm->sync)
|
||||
return ucsi->ppm->sync(ucsi->ppm);
|
||||
return 0;
|
||||
u64 ctrl;
|
||||
|
||||
ctrl = UCSI_ACK_CC_CI;
|
||||
ctrl |= UCSI_ACK_COMMAND_COMPLETE;
|
||||
|
||||
return ucsi->ops->sync_write(ucsi, UCSI_CONTROL, &ctrl, sizeof(ctrl));
|
||||
}
|
||||
|
||||
static int ucsi_command(struct ucsi *ucsi, struct ucsi_control *ctrl)
|
||||
static int ucsi_acknowledge_connector_change(struct ucsi *ucsi)
|
||||
{
|
||||
int ret;
|
||||
u64 ctrl;
|
||||
|
||||
trace_ucsi_command(ctrl);
|
||||
ctrl = UCSI_ACK_CC_CI;
|
||||
ctrl |= UCSI_ACK_CONNECTOR_CHANGE;
|
||||
|
||||
set_bit(COMMAND_PENDING, &ucsi->flags);
|
||||
|
||||
ret = ucsi->ppm->cmd(ucsi->ppm, ctrl);
|
||||
if (ret)
|
||||
goto err_clear_flag;
|
||||
|
||||
if (!wait_for_completion_timeout(&ucsi->complete,
|
||||
msecs_to_jiffies(UCSI_TIMEOUT_MS))) {
|
||||
dev_warn(ucsi->dev, "PPM NOT RESPONDING\n");
|
||||
ret = -ETIMEDOUT;
|
||||
}
|
||||
|
||||
err_clear_flag:
|
||||
clear_bit(COMMAND_PENDING, &ucsi->flags);
|
||||
|
||||
return ret;
|
||||
return ucsi->ops->async_write(ucsi, UCSI_CONTROL, &ctrl, sizeof(ctrl));
|
||||
}
|
||||
|
||||
static int ucsi_ack(struct ucsi *ucsi, u8 ack)
|
||||
static int ucsi_exec_command(struct ucsi *ucsi, u64 command);
|
||||
|
||||
static int ucsi_read_error(struct ucsi *ucsi)
|
||||
{
|
||||
struct ucsi_control ctrl;
|
||||
int ret;
|
||||
|
||||
trace_ucsi_ack(ack);
|
||||
|
||||
set_bit(ACK_PENDING, &ucsi->flags);
|
||||
|
||||
UCSI_CMD_ACK(ctrl, ack);
|
||||
ret = ucsi->ppm->cmd(ucsi->ppm, &ctrl);
|
||||
if (ret)
|
||||
goto out_clear_bit;
|
||||
|
||||
/* Waiting for ACK with ACK CMD, but not with EVENT for now */
|
||||
if (ack == UCSI_ACK_EVENT)
|
||||
goto out_clear_bit;
|
||||
|
||||
if (!wait_for_completion_timeout(&ucsi->complete,
|
||||
msecs_to_jiffies(UCSI_TIMEOUT_MS)))
|
||||
ret = -ETIMEDOUT;
|
||||
|
||||
out_clear_bit:
|
||||
clear_bit(ACK_PENDING, &ucsi->flags);
|
||||
|
||||
if (ret)
|
||||
dev_err(ucsi->dev, "%s: failed\n", __func__);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ucsi_run_command(struct ucsi *ucsi, struct ucsi_control *ctrl,
|
||||
void *data, size_t size)
|
||||
{
|
||||
struct ucsi_control _ctrl;
|
||||
u8 data_length;
|
||||
u16 error;
|
||||
int ret;
|
||||
|
||||
ret = ucsi_command(ucsi, ctrl);
|
||||
/* Acknowlege the command that failed */
|
||||
ret = ucsi_acknowledge_command(ucsi);
|
||||
if (ret)
|
||||
goto err;
|
||||
return ret;
|
||||
|
||||
switch (ucsi->status) {
|
||||
case UCSI_IDLE:
|
||||
ret = ucsi_sync(ucsi);
|
||||
if (ret)
|
||||
dev_warn(ucsi->dev, "%s: sync failed\n", __func__);
|
||||
ret = ucsi_exec_command(ucsi, UCSI_GET_ERROR_STATUS);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (data)
|
||||
memcpy(data, ucsi->ppm->data->message_in, size);
|
||||
ret = ucsi->ops->read(ucsi, UCSI_MESSAGE_IN, &error, sizeof(error));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
data_length = ucsi->ppm->data->cci.data_length;
|
||||
|
||||
ret = ucsi_ack(ucsi, UCSI_ACK_CMD);
|
||||
if (!ret)
|
||||
ret = data_length;
|
||||
switch (error) {
|
||||
case UCSI_ERROR_INCOMPATIBLE_PARTNER:
|
||||
return -EOPNOTSUPP;
|
||||
case UCSI_ERROR_CC_COMMUNICATION_ERR:
|
||||
return -ECOMM;
|
||||
case UCSI_ERROR_CONTRACT_NEGOTIATION_FAIL:
|
||||
return -EPROTO;
|
||||
case UCSI_ERROR_DEAD_BATTERY:
|
||||
dev_warn(ucsi->dev, "Dead battery condition!\n");
|
||||
return -EPERM;
|
||||
case UCSI_ERROR_INVALID_CON_NUM:
|
||||
case UCSI_ERROR_UNREGONIZED_CMD:
|
||||
case UCSI_ERROR_INVALID_CMD_ARGUMENT:
|
||||
dev_err(ucsi->dev, "possible UCSI driver bug %u\n", error);
|
||||
return -EINVAL;
|
||||
case UCSI_ERROR_OVERCURRENT:
|
||||
dev_warn(ucsi->dev, "Overcurrent condition\n");
|
||||
break;
|
||||
case UCSI_BUSY:
|
||||
/* The caller decides whether to cancel or not */
|
||||
ret = -EBUSY;
|
||||
case UCSI_ERROR_PARTNER_REJECTED_SWAP:
|
||||
dev_warn(ucsi->dev, "Partner rejected swap\n");
|
||||
break;
|
||||
case UCSI_ERROR:
|
||||
ret = ucsi_ack(ucsi, UCSI_ACK_CMD);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
_ctrl.raw_cmd = 0;
|
||||
_ctrl.cmd.cmd = UCSI_GET_ERROR_STATUS;
|
||||
ret = ucsi_command(ucsi, &_ctrl);
|
||||
if (ret) {
|
||||
dev_err(ucsi->dev, "reading error failed!\n");
|
||||
break;
|
||||
}
|
||||
|
||||
memcpy(&error, ucsi->ppm->data->message_in, sizeof(error));
|
||||
|
||||
/* Something has really gone wrong */
|
||||
if (WARN_ON(ucsi->status == UCSI_ERROR)) {
|
||||
ret = -ENODEV;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = ucsi_ack(ucsi, UCSI_ACK_CMD);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
switch (error) {
|
||||
case UCSI_ERROR_INCOMPATIBLE_PARTNER:
|
||||
ret = -EOPNOTSUPP;
|
||||
break;
|
||||
case UCSI_ERROR_CC_COMMUNICATION_ERR:
|
||||
ret = -ECOMM;
|
||||
break;
|
||||
case UCSI_ERROR_CONTRACT_NEGOTIATION_FAIL:
|
||||
ret = -EPROTO;
|
||||
break;
|
||||
case UCSI_ERROR_DEAD_BATTERY:
|
||||
dev_warn(ucsi->dev, "Dead battery condition!\n");
|
||||
ret = -EPERM;
|
||||
break;
|
||||
/* The following mean a bug in this driver */
|
||||
case UCSI_ERROR_INVALID_CON_NUM:
|
||||
case UCSI_ERROR_UNREGONIZED_CMD:
|
||||
case UCSI_ERROR_INVALID_CMD_ARGUMENT:
|
||||
dev_warn(ucsi->dev,
|
||||
"%s: possible UCSI driver bug - error 0x%x\n",
|
||||
__func__, error);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
default:
|
||||
dev_warn(ucsi->dev,
|
||||
"%s: error without status\n", __func__);
|
||||
ret = -EIO;
|
||||
break;
|
||||
}
|
||||
case UCSI_ERROR_HARD_RESET:
|
||||
dev_warn(ucsi->dev, "Hard reset occurred\n");
|
||||
break;
|
||||
case UCSI_ERROR_PPM_POLICY_CONFLICT:
|
||||
dev_warn(ucsi->dev, "PPM Policy conflict\n");
|
||||
break;
|
||||
case UCSI_ERROR_SWAP_REJECTED:
|
||||
dev_warn(ucsi->dev, "Swap rejected\n");
|
||||
break;
|
||||
case UCSI_ERROR_UNDEFINED:
|
||||
default:
|
||||
dev_err(ucsi->dev, "unknown error %u\n", error);
|
||||
break;
|
||||
}
|
||||
|
||||
err:
|
||||
trace_ucsi_run_command(ctrl, ret);
|
||||
|
||||
return ret;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
int ucsi_send_command(struct ucsi *ucsi, struct ucsi_control *ctrl,
|
||||
static int ucsi_exec_command(struct ucsi *ucsi, u64 cmd)
|
||||
{
|
||||
u32 cci;
|
||||
int ret;
|
||||
|
||||
ret = ucsi->ops->sync_write(ucsi, UCSI_CONTROL, &cmd, sizeof(cmd));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ucsi->ops->read(ucsi, UCSI_CCI, &cci, sizeof(cci));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (cci & UCSI_CCI_BUSY)
|
||||
return -EBUSY;
|
||||
|
||||
if (!(cci & UCSI_CCI_COMMAND_COMPLETE))
|
||||
return -EIO;
|
||||
|
||||
if (cci & UCSI_CCI_NOT_SUPPORTED)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (cci & UCSI_CCI_ERROR) {
|
||||
if (cmd == UCSI_GET_ERROR_STATUS)
|
||||
return -EIO;
|
||||
return ucsi_read_error(ucsi);
|
||||
}
|
||||
|
||||
return UCSI_CCI_LENGTH(cci);
|
||||
}
|
||||
|
||||
static int ucsi_run_command(struct ucsi *ucsi, u64 command,
|
||||
void *data, size_t size)
|
||||
{
|
||||
u8 length;
|
||||
int ret;
|
||||
|
||||
ret = ucsi_exec_command(ucsi, command);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
length = ret;
|
||||
|
||||
if (data) {
|
||||
ret = ucsi->ops->read(ucsi, UCSI_MESSAGE_IN, data, size);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = ucsi_acknowledge_command(ucsi);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
int ucsi_send_command(struct ucsi *ucsi, u64 command,
|
||||
void *retval, size_t size)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&ucsi->ppm_lock);
|
||||
ret = ucsi_run_command(ucsi, ctrl, retval, size);
|
||||
ret = ucsi_run_command(ucsi, command, retval, size);
|
||||
mutex_unlock(&ucsi->ppm_lock);
|
||||
|
||||
return ret;
|
||||
|
|
@ -207,11 +186,12 @@ EXPORT_SYMBOL_GPL(ucsi_send_command);
|
|||
|
||||
int ucsi_resume(struct ucsi *ucsi)
|
||||
{
|
||||
struct ucsi_control ctrl;
|
||||
u64 command;
|
||||
|
||||
/* Restore UCSI notification enable mask after system resume */
|
||||
UCSI_CMD_SET_NTFY_ENABLE(ctrl, UCSI_ENABLE_NTFY_ALL);
|
||||
return ucsi_send_command(ucsi, &ctrl, NULL, 0);
|
||||
command = UCSI_SET_NOTIFICATION_ENABLE | UCSI_ENABLE_NTFY_ALL;
|
||||
|
||||
return ucsi_send_command(ucsi, command, NULL, 0);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ucsi_resume);
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
|
@ -219,15 +199,15 @@ EXPORT_SYMBOL_GPL(ucsi_resume);
|
|||
void ucsi_altmode_update_active(struct ucsi_connector *con)
|
||||
{
|
||||
const struct typec_altmode *altmode = NULL;
|
||||
struct ucsi_control ctrl;
|
||||
u64 command;
|
||||
int ret;
|
||||
u8 cur;
|
||||
int i;
|
||||
|
||||
UCSI_CMD_GET_CURRENT_CAM(ctrl, con->num);
|
||||
ret = ucsi_run_command(con->ucsi, &ctrl, &cur, sizeof(cur));
|
||||
command = UCSI_GET_CURRENT_CAM | UCSI_CONNECTOR_NUMBER(con->num);
|
||||
ret = ucsi_run_command(con->ucsi, command, &cur, sizeof(cur));
|
||||
if (ret < 0) {
|
||||
if (con->ucsi->ppm->data->version > 0x0100) {
|
||||
if (con->ucsi->version > 0x0100) {
|
||||
dev_err(con->ucsi->dev,
|
||||
"GET_CURRENT_CAM command failed\n");
|
||||
return;
|
||||
|
|
@ -343,7 +323,7 @@ static int ucsi_register_altmodes(struct ucsi_connector *con, u8 recipient)
|
|||
int max_altmodes = UCSI_MAX_ALTMODES;
|
||||
struct typec_altmode_desc desc;
|
||||
struct ucsi_altmode alt[2];
|
||||
struct ucsi_control ctrl;
|
||||
u64 command;
|
||||
int num = 1;
|
||||
int ret;
|
||||
int len;
|
||||
|
|
@ -361,8 +341,11 @@ static int ucsi_register_altmodes(struct ucsi_connector *con, u8 recipient)
|
|||
|
||||
for (i = 0; i < max_altmodes;) {
|
||||
memset(alt, 0, sizeof(alt));
|
||||
UCSI_CMD_GET_ALTERNATE_MODES(ctrl, recipient, con->num, i, 1);
|
||||
len = ucsi_run_command(con->ucsi, &ctrl, alt, sizeof(alt));
|
||||
command = UCSI_GET_ALTERNATE_MODES;
|
||||
command |= UCSI_GET_ALTMODE_RECIPIENT(recipient);
|
||||
command |= UCSI_GET_ALTMODE_CONNECTOR_NUMBER(con->num);
|
||||
command |= UCSI_GET_ALTMODE_OFFSET(i);
|
||||
len = ucsi_run_command(con->ucsi, command, alt, sizeof(alt));
|
||||
if (len <= 0)
|
||||
return len;
|
||||
|
||||
|
|
@ -424,7 +407,7 @@ static void ucsi_unregister_altmodes(struct ucsi_connector *con, u8 recipient)
|
|||
|
||||
static void ucsi_pwr_opmode_change(struct ucsi_connector *con)
|
||||
{
|
||||
switch (con->status.pwr_op_mode) {
|
||||
switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) {
|
||||
case UCSI_CONSTAT_PWR_OPMODE_PD:
|
||||
typec_set_pwr_opmode(con->port, TYPEC_PWR_MODE_PD);
|
||||
break;
|
||||
|
|
@ -442,6 +425,7 @@ static void ucsi_pwr_opmode_change(struct ucsi_connector *con)
|
|||
|
||||
static int ucsi_register_partner(struct ucsi_connector *con)
|
||||
{
|
||||
u8 pwr_opmode = UCSI_CONSTAT_PWR_OPMODE(con->status.flags);
|
||||
struct typec_partner_desc desc;
|
||||
struct typec_partner *partner;
|
||||
|
||||
|
|
@ -450,7 +434,7 @@ static int ucsi_register_partner(struct ucsi_connector *con)
|
|||
|
||||
memset(&desc, 0, sizeof(desc));
|
||||
|
||||
switch (con->status.partner_type) {
|
||||
switch (UCSI_CONSTAT_PARTNER_TYPE(con->status.flags)) {
|
||||
case UCSI_CONSTAT_PARTNER_TYPE_DEBUG:
|
||||
desc.accessory = TYPEC_ACCESSORY_DEBUG;
|
||||
break;
|
||||
|
|
@ -461,7 +445,7 @@ static int ucsi_register_partner(struct ucsi_connector *con)
|
|||
break;
|
||||
}
|
||||
|
||||
desc.usb_pd = con->status.pwr_op_mode == UCSI_CONSTAT_PWR_OPMODE_PD;
|
||||
desc.usb_pd = pwr_opmode == UCSI_CONSTAT_PWR_OPMODE_PD;
|
||||
|
||||
partner = typec_register_partner(con->port, &desc);
|
||||
if (IS_ERR(partner)) {
|
||||
|
|
@ -493,7 +477,7 @@ static void ucsi_partner_change(struct ucsi_connector *con)
|
|||
if (!con->partner)
|
||||
return;
|
||||
|
||||
switch (con->status.partner_type) {
|
||||
switch (UCSI_CONSTAT_PARTNER_TYPE(con->status.flags)) {
|
||||
case UCSI_CONSTAT_PARTNER_TYPE_UFP:
|
||||
typec_set_data_role(con->port, TYPEC_HOST);
|
||||
break;
|
||||
|
|
@ -518,29 +502,33 @@ static void ucsi_partner_change(struct ucsi_connector *con)
|
|||
ucsi_altmode_update_active(con);
|
||||
}
|
||||
|
||||
static void ucsi_connector_change(struct work_struct *work)
|
||||
static void ucsi_handle_connector_change(struct work_struct *work)
|
||||
{
|
||||
struct ucsi_connector *con = container_of(work, struct ucsi_connector,
|
||||
work);
|
||||
struct ucsi *ucsi = con->ucsi;
|
||||
struct ucsi_control ctrl;
|
||||
enum typec_role role;
|
||||
u64 command;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&con->lock);
|
||||
|
||||
UCSI_CMD_GET_CONNECTOR_STATUS(ctrl, con->num);
|
||||
ret = ucsi_send_command(ucsi, &ctrl, &con->status, sizeof(con->status));
|
||||
command = UCSI_GET_CONNECTOR_STATUS | UCSI_CONNECTOR_NUMBER(con->num);
|
||||
ret = ucsi_send_command(ucsi, command, &con->status,
|
||||
sizeof(con->status));
|
||||
if (ret < 0) {
|
||||
dev_err(ucsi->dev, "%s: GET_CONNECTOR_STATUS failed (%d)\n",
|
||||
__func__, ret);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
role = !!(con->status.flags & UCSI_CONSTAT_PWR_DIR);
|
||||
|
||||
if (con->status.change & UCSI_CONSTAT_POWER_OPMODE_CHANGE)
|
||||
ucsi_pwr_opmode_change(con);
|
||||
|
||||
if (con->status.change & UCSI_CONSTAT_POWER_DIR_CHANGE) {
|
||||
typec_set_pwr_role(con->port, con->status.pwr_dir);
|
||||
typec_set_pwr_role(con->port, role);
|
||||
|
||||
/* Complete pending power role swap */
|
||||
if (!completion_done(&con->complete))
|
||||
|
|
@ -548,9 +536,9 @@ static void ucsi_connector_change(struct work_struct *work)
|
|||
}
|
||||
|
||||
if (con->status.change & UCSI_CONSTAT_CONNECT_CHANGE) {
|
||||
typec_set_pwr_role(con->port, con->status.pwr_dir);
|
||||
typec_set_pwr_role(con->port, role);
|
||||
|
||||
switch (con->status.partner_type) {
|
||||
switch (UCSI_CONSTAT_PARTNER_TYPE(con->status.flags)) {
|
||||
case UCSI_CONSTAT_PARTNER_TYPE_UFP:
|
||||
typec_set_data_role(con->port, TYPEC_HOST);
|
||||
break;
|
||||
|
|
@ -561,7 +549,7 @@ static void ucsi_connector_change(struct work_struct *work)
|
|||
break;
|
||||
}
|
||||
|
||||
if (con->status.connected)
|
||||
if (con->status.flags & UCSI_CONSTAT_CONNECTED)
|
||||
ucsi_register_partner(con);
|
||||
else
|
||||
ucsi_unregister_partner(con);
|
||||
|
|
@ -573,14 +561,15 @@ static void ucsi_connector_change(struct work_struct *work)
|
|||
* Running GET_CAM_SUPPORTED command just to make sure the PPM
|
||||
* does not get stuck in case it assumes we do so.
|
||||
*/
|
||||
UCSI_CMD_GET_CAM_SUPPORTED(ctrl, con->num);
|
||||
ucsi_run_command(con->ucsi, &ctrl, NULL, 0);
|
||||
command = UCSI_GET_CAM_SUPPORTED;
|
||||
command |= UCSI_CONNECTOR_NUMBER(con->num);
|
||||
ucsi_run_command(con->ucsi, command, NULL, 0);
|
||||
}
|
||||
|
||||
if (con->status.change & UCSI_CONSTAT_PARTNER_CHANGE)
|
||||
ucsi_partner_change(con);
|
||||
|
||||
ret = ucsi_ack(ucsi, UCSI_ACK_EVENT);
|
||||
ret = ucsi_acknowledge_connector_change(ucsi);
|
||||
if (ret)
|
||||
dev_err(ucsi->dev, "%s: ACK failed (%d)", __func__, ret);
|
||||
|
||||
|
|
@ -592,117 +581,83 @@ out_unlock:
|
|||
}
|
||||
|
||||
/**
|
||||
* ucsi_notify - PPM notification handler
|
||||
* @ucsi: Source UCSI Interface for the notifications
|
||||
*
|
||||
* Handle notifications from PPM of @ucsi.
|
||||
* ucsi_connector_change - Process Connector Change Event
|
||||
* @ucsi: UCSI Interface
|
||||
* @num: Connector number
|
||||
*/
|
||||
void ucsi_notify(struct ucsi *ucsi)
|
||||
void ucsi_connector_change(struct ucsi *ucsi, u8 num)
|
||||
{
|
||||
struct ucsi_cci *cci;
|
||||
struct ucsi_connector *con = &ucsi->connector[num - 1];
|
||||
|
||||
/* There is no requirement to sync here, but no harm either. */
|
||||
ucsi_sync(ucsi);
|
||||
|
||||
cci = &ucsi->ppm->data->cci;
|
||||
|
||||
if (cci->error)
|
||||
ucsi->status = UCSI_ERROR;
|
||||
else if (cci->busy)
|
||||
ucsi->status = UCSI_BUSY;
|
||||
else
|
||||
ucsi->status = UCSI_IDLE;
|
||||
|
||||
if (cci->cmd_complete && test_bit(COMMAND_PENDING, &ucsi->flags)) {
|
||||
complete(&ucsi->complete);
|
||||
} else if (cci->ack_complete && test_bit(ACK_PENDING, &ucsi->flags)) {
|
||||
complete(&ucsi->complete);
|
||||
} else if (cci->connector_change) {
|
||||
struct ucsi_connector *con;
|
||||
|
||||
con = &ucsi->connector[cci->connector_change - 1];
|
||||
|
||||
if (!test_and_set_bit(EVENT_PENDING, &ucsi->flags))
|
||||
schedule_work(&con->work);
|
||||
}
|
||||
|
||||
trace_ucsi_notify(ucsi->ppm->data->raw_cci);
|
||||
if (!test_and_set_bit(EVENT_PENDING, &ucsi->flags))
|
||||
schedule_work(&con->work);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ucsi_notify);
|
||||
EXPORT_SYMBOL_GPL(ucsi_connector_change);
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
static int ucsi_reset_connector(struct ucsi_connector *con, bool hard)
|
||||
{
|
||||
struct ucsi_control ctrl;
|
||||
u64 command;
|
||||
|
||||
UCSI_CMD_CONNECTOR_RESET(ctrl, con, hard);
|
||||
command = UCSI_CONNECTOR_RESET | UCSI_CONNECTOR_NUMBER(con->num);
|
||||
command |= hard ? UCSI_CONNECTOR_RESET_HARD : 0;
|
||||
|
||||
return ucsi_send_command(con->ucsi, &ctrl, NULL, 0);
|
||||
return ucsi_send_command(con->ucsi, command, NULL, 0);
|
||||
}
|
||||
|
||||
static int ucsi_reset_ppm(struct ucsi *ucsi)
|
||||
{
|
||||
struct ucsi_control ctrl;
|
||||
u64 command = UCSI_PPM_RESET;
|
||||
unsigned long tmo;
|
||||
u32 cci;
|
||||
int ret;
|
||||
|
||||
ctrl.raw_cmd = 0;
|
||||
ctrl.cmd.cmd = UCSI_PPM_RESET;
|
||||
trace_ucsi_command(&ctrl);
|
||||
ret = ucsi->ppm->cmd(ucsi->ppm, &ctrl);
|
||||
if (ret)
|
||||
goto err;
|
||||
ret = ucsi->ops->async_write(ucsi, UCSI_CONTROL, &command,
|
||||
sizeof(command));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
tmo = jiffies + msecs_to_jiffies(UCSI_TIMEOUT_MS);
|
||||
|
||||
do {
|
||||
/* Here sync is critical. */
|
||||
ret = ucsi_sync(ucsi);
|
||||
if (ret)
|
||||
goto err;
|
||||
if (time_is_before_jiffies(tmo))
|
||||
return -ETIMEDOUT;
|
||||
|
||||
if (ucsi->ppm->data->cci.reset_complete)
|
||||
break;
|
||||
ret = ucsi->ops->read(ucsi, UCSI_CCI, &cci, sizeof(cci));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* If the PPM is still doing something else, reset it again. */
|
||||
if (ucsi->ppm->data->raw_cci) {
|
||||
dev_warn_ratelimited(ucsi->dev,
|
||||
"Failed to reset PPM! Trying again..\n");
|
||||
|
||||
trace_ucsi_command(&ctrl);
|
||||
ret = ucsi->ppm->cmd(ucsi->ppm, &ctrl);
|
||||
if (ret)
|
||||
goto err;
|
||||
if (cci & ~UCSI_CCI_RESET_COMPLETE) {
|
||||
ret = ucsi->ops->async_write(ucsi, UCSI_CONTROL,
|
||||
&command,
|
||||
sizeof(command));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Letting the PPM settle down. */
|
||||
msleep(20);
|
||||
} while (!(cci & UCSI_CCI_RESET_COMPLETE));
|
||||
|
||||
ret = -ETIMEDOUT;
|
||||
} while (time_is_after_jiffies(tmo));
|
||||
|
||||
err:
|
||||
trace_ucsi_reset_ppm(&ctrl, ret);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ucsi_role_cmd(struct ucsi_connector *con, struct ucsi_control *ctrl)
|
||||
static int ucsi_role_cmd(struct ucsi_connector *con, u64 command)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ucsi_send_command(con->ucsi, ctrl, NULL, 0);
|
||||
ret = ucsi_send_command(con->ucsi, command, NULL, 0);
|
||||
if (ret == -ETIMEDOUT) {
|
||||
struct ucsi_control c;
|
||||
u64 c;
|
||||
|
||||
/* PPM most likely stopped responding. Resetting everything. */
|
||||
mutex_lock(&con->ucsi->ppm_lock);
|
||||
ucsi_reset_ppm(con->ucsi);
|
||||
mutex_unlock(&con->ucsi->ppm_lock);
|
||||
|
||||
UCSI_CMD_SET_NTFY_ENABLE(c, UCSI_ENABLE_NTFY_ALL);
|
||||
ucsi_send_command(con->ucsi, &c, NULL, 0);
|
||||
c = UCSI_SET_NOTIFICATION_ENABLE | UCSI_ENABLE_NTFY_ALL;
|
||||
ucsi_send_command(con->ucsi, c, NULL, 0);
|
||||
|
||||
ucsi_reset_connector(con, true);
|
||||
}
|
||||
|
|
@ -713,7 +668,8 @@ static int ucsi_role_cmd(struct ucsi_connector *con, struct ucsi_control *ctrl)
|
|||
static int ucsi_dr_swap(struct typec_port *port, enum typec_data_role role)
|
||||
{
|
||||
struct ucsi_connector *con = typec_get_drvdata(port);
|
||||
struct ucsi_control ctrl;
|
||||
u8 partner_type;
|
||||
u64 command;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&con->lock);
|
||||
|
|
@ -723,14 +679,17 @@ static int ucsi_dr_swap(struct typec_port *port, enum typec_data_role role)
|
|||
goto out_unlock;
|
||||
}
|
||||
|
||||
if ((con->status.partner_type == UCSI_CONSTAT_PARTNER_TYPE_DFP &&
|
||||
partner_type = UCSI_CONSTAT_PARTNER_TYPE(con->status.flags);
|
||||
if ((partner_type == UCSI_CONSTAT_PARTNER_TYPE_DFP &&
|
||||
role == TYPEC_DEVICE) ||
|
||||
(con->status.partner_type == UCSI_CONSTAT_PARTNER_TYPE_UFP &&
|
||||
(partner_type == UCSI_CONSTAT_PARTNER_TYPE_UFP &&
|
||||
role == TYPEC_HOST))
|
||||
goto out_unlock;
|
||||
|
||||
UCSI_CMD_SET_UOR(ctrl, con, role);
|
||||
ret = ucsi_role_cmd(con, &ctrl);
|
||||
command = UCSI_SET_UOR | UCSI_CONNECTOR_NUMBER(con->num);
|
||||
command |= UCSI_SET_UOR_ROLE(role);
|
||||
command |= UCSI_SET_UOR_ACCEPT_ROLE_SWAPS;
|
||||
ret = ucsi_role_cmd(con, command);
|
||||
if (ret < 0)
|
||||
goto out_unlock;
|
||||
|
||||
|
|
@ -747,7 +706,8 @@ out_unlock:
|
|||
static int ucsi_pr_swap(struct typec_port *port, enum typec_role role)
|
||||
{
|
||||
struct ucsi_connector *con = typec_get_drvdata(port);
|
||||
struct ucsi_control ctrl;
|
||||
enum typec_role cur_role;
|
||||
u64 command;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&con->lock);
|
||||
|
|
@ -757,11 +717,15 @@ static int ucsi_pr_swap(struct typec_port *port, enum typec_role role)
|
|||
goto out_unlock;
|
||||
}
|
||||
|
||||
if (con->status.pwr_dir == role)
|
||||
cur_role = !!(con->status.flags & UCSI_CONSTAT_PWR_DIR);
|
||||
|
||||
if (cur_role == role)
|
||||
goto out_unlock;
|
||||
|
||||
UCSI_CMD_SET_PDR(ctrl, con, role);
|
||||
ret = ucsi_role_cmd(con, &ctrl);
|
||||
command = UCSI_SET_PDR | UCSI_CONNECTOR_NUMBER(con->num);
|
||||
command |= UCSI_SET_PDR_ROLE(role);
|
||||
command |= UCSI_SET_PDR_ACCEPT_ROLE_SWAPS;
|
||||
ret = ucsi_role_cmd(con, command);
|
||||
if (ret < 0)
|
||||
goto out_unlock;
|
||||
|
||||
|
|
@ -772,7 +736,8 @@ static int ucsi_pr_swap(struct typec_port *port, enum typec_role role)
|
|||
}
|
||||
|
||||
/* Something has gone wrong while swapping the role */
|
||||
if (con->status.pwr_op_mode != UCSI_CONSTAT_PWR_OPMODE_PD) {
|
||||
if (UCSI_CONSTAT_PWR_OPMODE(con->status.flags) !=
|
||||
UCSI_CONSTAT_PWR_OPMODE_PD) {
|
||||
ucsi_reset_connector(con, true);
|
||||
ret = -EPROTO;
|
||||
}
|
||||
|
|
@ -804,18 +769,19 @@ static int ucsi_register_port(struct ucsi *ucsi, int index)
|
|||
struct ucsi_connector *con = &ucsi->connector[index];
|
||||
struct typec_capability *cap = &con->typec_cap;
|
||||
enum typec_accessory *accessory = cap->accessory;
|
||||
struct ucsi_control ctrl;
|
||||
u64 command;
|
||||
int ret;
|
||||
|
||||
INIT_WORK(&con->work, ucsi_connector_change);
|
||||
INIT_WORK(&con->work, ucsi_handle_connector_change);
|
||||
init_completion(&con->complete);
|
||||
mutex_init(&con->lock);
|
||||
con->num = index + 1;
|
||||
con->ucsi = ucsi;
|
||||
|
||||
/* Get connector capability */
|
||||
UCSI_CMD_GET_CONNECTOR_CAPABILITY(ctrl, con->num);
|
||||
ret = ucsi_run_command(ucsi, &ctrl, &con->cap, sizeof(con->cap));
|
||||
command = UCSI_GET_CONNECTOR_CAPABILITY;
|
||||
command |= UCSI_CONNECTOR_NUMBER(con->num);
|
||||
ret = ucsi_run_command(ucsi, command, &con->cap, sizeof(con->cap));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
|
@ -826,11 +792,12 @@ static int ucsi_register_port(struct ucsi *ucsi, int index)
|
|||
else if (con->cap.op_mode & UCSI_CONCAP_OPMODE_UFP)
|
||||
cap->data = TYPEC_PORT_UFP;
|
||||
|
||||
if (con->cap.provider && con->cap.consumer)
|
||||
if ((con->cap.flags & UCSI_CONCAP_FLAG_PROVIDER) &&
|
||||
(con->cap.flags & UCSI_CONCAP_FLAG_CONSUMER))
|
||||
cap->type = TYPEC_PORT_DRP;
|
||||
else if (con->cap.provider)
|
||||
else if (con->cap.flags & UCSI_CONCAP_FLAG_PROVIDER)
|
||||
cap->type = TYPEC_PORT_SRC;
|
||||
else if (con->cap.consumer)
|
||||
else if (con->cap.flags & UCSI_CONCAP_FLAG_CONSUMER)
|
||||
cap->type = TYPEC_PORT_SNK;
|
||||
|
||||
cap->revision = ucsi->cap.typec_version;
|
||||
|
|
@ -858,17 +825,15 @@ static int ucsi_register_port(struct ucsi *ucsi, int index)
|
|||
con->num);
|
||||
|
||||
/* Get the status */
|
||||
UCSI_CMD_GET_CONNECTOR_STATUS(ctrl, con->num);
|
||||
ret = ucsi_run_command(ucsi, &ctrl, &con->status, sizeof(con->status));
|
||||
command = UCSI_GET_CONNECTOR_STATUS | UCSI_CONNECTOR_NUMBER(con->num);
|
||||
ret = ucsi_run_command(ucsi, command, &con->status,
|
||||
sizeof(con->status));
|
||||
if (ret < 0) {
|
||||
dev_err(ucsi->dev, "con%d: failed to get status\n", con->num);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ucsi_pwr_opmode_change(con);
|
||||
typec_set_pwr_role(con->port, con->status.pwr_dir);
|
||||
|
||||
switch (con->status.partner_type) {
|
||||
switch (UCSI_CONSTAT_PARTNER_TYPE(con->status.flags)) {
|
||||
case UCSI_CONSTAT_PARTNER_TYPE_UFP:
|
||||
typec_set_data_role(con->port, TYPEC_HOST);
|
||||
break;
|
||||
|
|
@ -880,8 +845,12 @@ static int ucsi_register_port(struct ucsi *ucsi, int index)
|
|||
}
|
||||
|
||||
/* Check if there is already something connected */
|
||||
if (con->status.connected)
|
||||
if (con->status.flags & UCSI_CONSTAT_CONNECTED) {
|
||||
typec_set_pwr_role(con->port,
|
||||
!!(con->status.flags & UCSI_CONSTAT_PWR_DIR));
|
||||
ucsi_pwr_opmode_change(con);
|
||||
ucsi_register_partner(con);
|
||||
}
|
||||
|
||||
if (con->partner) {
|
||||
ret = ucsi_register_altmodes(con, UCSI_RECIPIENT_SOP);
|
||||
|
|
@ -898,11 +867,16 @@ static int ucsi_register_port(struct ucsi *ucsi, int index)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void ucsi_init(struct work_struct *work)
|
||||
/**
|
||||
* ucsi_init - Initialize UCSI interface
|
||||
* @ucsi: UCSI to be initialized
|
||||
*
|
||||
* Registers all ports @ucsi has and enables all notification events.
|
||||
*/
|
||||
int ucsi_init(struct ucsi *ucsi)
|
||||
{
|
||||
struct ucsi *ucsi = container_of(work, struct ucsi, work);
|
||||
struct ucsi_connector *con;
|
||||
struct ucsi_control ctrl;
|
||||
u64 command;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
|
|
@ -916,15 +890,15 @@ static void ucsi_init(struct work_struct *work)
|
|||
}
|
||||
|
||||
/* Enable basic notifications */
|
||||
UCSI_CMD_SET_NTFY_ENABLE(ctrl, UCSI_ENABLE_NTFY_CMD_COMPLETE |
|
||||
UCSI_ENABLE_NTFY_ERROR);
|
||||
ret = ucsi_run_command(ucsi, &ctrl, NULL, 0);
|
||||
command = UCSI_SET_NOTIFICATION_ENABLE;
|
||||
command |= UCSI_ENABLE_NTFY_CMD_COMPLETE | UCSI_ENABLE_NTFY_ERROR;
|
||||
ret = ucsi_run_command(ucsi, command, NULL, 0);
|
||||
if (ret < 0)
|
||||
goto err_reset;
|
||||
|
||||
/* Get PPM capabilities */
|
||||
UCSI_CMD_GET_CAPABILITY(ctrl);
|
||||
ret = ucsi_run_command(ucsi, &ctrl, &ucsi->cap, sizeof(ucsi->cap));
|
||||
command = UCSI_GET_CAPABILITY;
|
||||
ret = ucsi_run_command(ucsi, command, &ucsi->cap, sizeof(ucsi->cap));
|
||||
if (ret < 0)
|
||||
goto err_reset;
|
||||
|
||||
|
|
@ -949,14 +923,14 @@ static void ucsi_init(struct work_struct *work)
|
|||
}
|
||||
|
||||
/* Enable all notifications */
|
||||
UCSI_CMD_SET_NTFY_ENABLE(ctrl, UCSI_ENABLE_NTFY_ALL);
|
||||
ret = ucsi_run_command(ucsi, &ctrl, NULL, 0);
|
||||
command = UCSI_SET_NOTIFICATION_ENABLE | UCSI_ENABLE_NTFY_ALL;
|
||||
ret = ucsi_run_command(ucsi, command, NULL, 0);
|
||||
if (ret < 0)
|
||||
goto err_unregister;
|
||||
|
||||
mutex_unlock(&ucsi->ppm_lock);
|
||||
|
||||
return;
|
||||
return 0;
|
||||
|
||||
err_unregister:
|
||||
for (con = ucsi->connector; con->port; con++) {
|
||||
|
|
@ -970,59 +944,115 @@ err_reset:
|
|||
ucsi_reset_ppm(ucsi);
|
||||
err:
|
||||
mutex_unlock(&ucsi->ppm_lock);
|
||||
dev_err(ucsi->dev, "PPM init failed (%d)\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ucsi_init);
|
||||
|
||||
static void ucsi_init_work(struct work_struct *work)
|
||||
{
|
||||
struct ucsi *ucsi = container_of(work, struct ucsi, work);
|
||||
int ret;
|
||||
|
||||
ret = ucsi_init(ucsi);
|
||||
if (ret)
|
||||
dev_err(ucsi->dev, "PPM init failed (%d)\n", ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* ucsi_register_ppm - Register UCSI PPM Interface
|
||||
* @dev: Device interface to the PPM
|
||||
* @ppm: The PPM interface
|
||||
*
|
||||
* Allocates UCSI instance, associates it with @ppm and returns it to the
|
||||
* caller, and schedules initialization of the interface.
|
||||
* ucsi_get_drvdata - Return private driver data pointer
|
||||
* @ucsi: UCSI interface
|
||||
*/
|
||||
struct ucsi *ucsi_register_ppm(struct device *dev, struct ucsi_ppm *ppm)
|
||||
void *ucsi_get_drvdata(struct ucsi *ucsi)
|
||||
{
|
||||
return ucsi->driver_data;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ucsi_get_drvdata);
|
||||
|
||||
/**
|
||||
* ucsi_get_drvdata - Assign private driver data pointer
|
||||
* @ucsi: UCSI interface
|
||||
* @data: Private data pointer
|
||||
*/
|
||||
void ucsi_set_drvdata(struct ucsi *ucsi, void *data)
|
||||
{
|
||||
ucsi->driver_data = data;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ucsi_set_drvdata);
|
||||
|
||||
/**
|
||||
* ucsi_create - Allocate UCSI instance
|
||||
* @dev: Device interface to the PPM (Platform Policy Manager)
|
||||
* @ops: I/O routines
|
||||
*/
|
||||
struct ucsi *ucsi_create(struct device *dev, const struct ucsi_operations *ops)
|
||||
{
|
||||
struct ucsi *ucsi;
|
||||
|
||||
if (!ops || !ops->read || !ops->sync_write || !ops->async_write)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
ucsi = kzalloc(sizeof(*ucsi), GFP_KERNEL);
|
||||
if (!ucsi)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
INIT_WORK(&ucsi->work, ucsi_init);
|
||||
init_completion(&ucsi->complete);
|
||||
INIT_WORK(&ucsi->work, ucsi_init_work);
|
||||
mutex_init(&ucsi->ppm_lock);
|
||||
|
||||
ucsi->dev = dev;
|
||||
ucsi->ppm = ppm;
|
||||
|
||||
/*
|
||||
* Communication with the PPM takes a lot of time. It is not reasonable
|
||||
* to initialize the driver here. Using a work for now.
|
||||
*/
|
||||
queue_work(system_long_wq, &ucsi->work);
|
||||
ucsi->ops = ops;
|
||||
|
||||
return ucsi;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ucsi_register_ppm);
|
||||
EXPORT_SYMBOL_GPL(ucsi_create);
|
||||
|
||||
/**
|
||||
* ucsi_unregister_ppm - Unregister UCSI PPM Interface
|
||||
* @ucsi: struct ucsi associated with the PPM
|
||||
*
|
||||
* Unregister UCSI PPM that was created with ucsi_register().
|
||||
* ucsi_destroy - Free UCSI instance
|
||||
* @ucsi: UCSI instance to be freed
|
||||
*/
|
||||
void ucsi_unregister_ppm(struct ucsi *ucsi)
|
||||
void ucsi_destroy(struct ucsi *ucsi)
|
||||
{
|
||||
struct ucsi_control ctrl;
|
||||
kfree(ucsi);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ucsi_destroy);
|
||||
|
||||
/**
|
||||
* ucsi_register - Register UCSI interface
|
||||
* @ucsi: UCSI instance
|
||||
*/
|
||||
int ucsi_register(struct ucsi *ucsi)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ucsi->ops->read(ucsi, UCSI_VERSION, &ucsi->version,
|
||||
sizeof(ucsi->version));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!ucsi->version)
|
||||
return -ENODEV;
|
||||
|
||||
queue_work(system_long_wq, &ucsi->work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ucsi_register);
|
||||
|
||||
/**
|
||||
* ucsi_unregister - Unregister UCSI interface
|
||||
* @ucsi: UCSI interface to be unregistered
|
||||
*
|
||||
* Unregister UCSI interface that was created with ucsi_register().
|
||||
*/
|
||||
void ucsi_unregister(struct ucsi *ucsi)
|
||||
{
|
||||
u64 cmd = UCSI_SET_NOTIFICATION_ENABLE;
|
||||
int i;
|
||||
|
||||
/* Make sure that we are not in the middle of driver initialization */
|
||||
cancel_work_sync(&ucsi->work);
|
||||
|
||||
/* Disable everything except command complete notification */
|
||||
UCSI_CMD_SET_NTFY_ENABLE(ctrl, UCSI_ENABLE_NTFY_CMD_COMPLETE)
|
||||
ucsi_send_command(ucsi, &ctrl, NULL, 0);
|
||||
/* Disable notifications */
|
||||
ucsi->ops->async_write(ucsi, UCSI_CONTROL, &cmd, sizeof(cmd));
|
||||
|
||||
for (i = 0; i < ucsi->cap.num_connectors; i++) {
|
||||
cancel_work_sync(&ucsi->connector[i].work);
|
||||
|
|
@ -1032,12 +1062,9 @@ void ucsi_unregister_ppm(struct ucsi *ucsi)
|
|||
typec_unregister_port(ucsi->connector[i].port);
|
||||
}
|
||||
|
||||
ucsi_reset_ppm(ucsi);
|
||||
|
||||
kfree(ucsi->connector);
|
||||
kfree(ucsi);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ucsi_unregister_ppm);
|
||||
EXPORT_SYMBOL_GPL(ucsi_unregister);
|
||||
|
||||
MODULE_AUTHOR("Heikki Krogerus <heikki.krogerus@linux.intel.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
|
|||
|
|
@ -10,177 +10,55 @@
|
|||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
/* Command Status and Connector Change Indication (CCI) data structure */
|
||||
struct ucsi_cci {
|
||||
u8:1; /* reserved */
|
||||
u8 connector_change:7;
|
||||
u8 data_length;
|
||||
u16:9; /* reserved */
|
||||
u16 not_supported:1;
|
||||
u16 cancel_complete:1;
|
||||
u16 reset_complete:1;
|
||||
u16 busy:1;
|
||||
u16 ack_complete:1;
|
||||
u16 error:1;
|
||||
u16 cmd_complete:1;
|
||||
} __packed;
|
||||
struct ucsi;
|
||||
|
||||
/* Default fields in CONTROL data structure */
|
||||
struct ucsi_command {
|
||||
u8 cmd;
|
||||
u8 length;
|
||||
u64 data:48;
|
||||
} __packed;
|
||||
/* UCSI offsets (Bytes) */
|
||||
#define UCSI_VERSION 0
|
||||
#define UCSI_CCI 4
|
||||
#define UCSI_CONTROL 8
|
||||
#define UCSI_MESSAGE_IN 16
|
||||
#define UCSI_MESSAGE_OUT 32
|
||||
|
||||
/* ACK Command structure */
|
||||
struct ucsi_ack_cmd {
|
||||
u8 cmd;
|
||||
u8 length;
|
||||
u8 cci_ack:1;
|
||||
u8 cmd_ack:1;
|
||||
u8:6; /* reserved */
|
||||
} __packed;
|
||||
/* Command Status and Connector Change Indication (CCI) bits */
|
||||
#define UCSI_CCI_CONNECTOR(_c_) (((_c_) & GENMASK(7, 0)) >> 1)
|
||||
#define UCSI_CCI_LENGTH(_c_) (((_c_) & GENMASK(15, 8)) >> 8)
|
||||
#define UCSI_CCI_NOT_SUPPORTED BIT(25)
|
||||
#define UCSI_CCI_CANCEL_COMPLETE BIT(26)
|
||||
#define UCSI_CCI_RESET_COMPLETE BIT(27)
|
||||
#define UCSI_CCI_BUSY BIT(28)
|
||||
#define UCSI_CCI_ACK_COMPLETE BIT(29)
|
||||
#define UCSI_CCI_ERROR BIT(30)
|
||||
#define UCSI_CCI_COMMAND_COMPLETE BIT(31)
|
||||
|
||||
/* Connector Reset Command structure */
|
||||
struct ucsi_con_rst {
|
||||
u8 cmd;
|
||||
u8 length;
|
||||
u8 con_num:7;
|
||||
u8 hard_reset:1;
|
||||
} __packed;
|
||||
|
||||
/* Set USB Operation Mode Command structure */
|
||||
struct ucsi_uor_cmd {
|
||||
u8 cmd;
|
||||
u8 length;
|
||||
u16 con_num:7;
|
||||
u16 role:3;
|
||||
#define UCSI_UOR_ROLE_DFP BIT(0)
|
||||
#define UCSI_UOR_ROLE_UFP BIT(1)
|
||||
#define UCSI_UOR_ROLE_DRP BIT(2)
|
||||
u16:6; /* reserved */
|
||||
} __packed;
|
||||
|
||||
/* Get Alternate Modes Command structure */
|
||||
struct ucsi_altmode_cmd {
|
||||
u8 cmd;
|
||||
u8 length;
|
||||
u8 recipient;
|
||||
#define UCSI_RECIPIENT_CON 0
|
||||
#define UCSI_RECIPIENT_SOP 1
|
||||
#define UCSI_RECIPIENT_SOP_P 2
|
||||
#define UCSI_RECIPIENT_SOP_PP 3
|
||||
u8 con_num;
|
||||
u8 offset;
|
||||
u8 num_altmodes;
|
||||
} __packed;
|
||||
|
||||
struct ucsi_control {
|
||||
union {
|
||||
u64 raw_cmd;
|
||||
struct ucsi_command cmd;
|
||||
struct ucsi_uor_cmd uor;
|
||||
struct ucsi_ack_cmd ack;
|
||||
struct ucsi_con_rst con_rst;
|
||||
struct ucsi_altmode_cmd alt;
|
||||
};
|
||||
/**
|
||||
* struct ucsi_operations - UCSI I/O operations
|
||||
* @read: Read operation
|
||||
* @sync_write: Blocking write operation
|
||||
* @async_write: Non-blocking write operation
|
||||
*
|
||||
* Read and write routines for UCSI interface. @sync_write must wait for the
|
||||
* Command Completion Event from the PPM before returning, and @async_write must
|
||||
* return immediately after sending the data to the PPM.
|
||||
*/
|
||||
struct ucsi_operations {
|
||||
int (*read)(struct ucsi *ucsi, unsigned int offset,
|
||||
void *val, size_t val_len);
|
||||
int (*sync_write)(struct ucsi *ucsi, unsigned int offset,
|
||||
const void *val, size_t val_len);
|
||||
int (*async_write)(struct ucsi *ucsi, unsigned int offset,
|
||||
const void *val, size_t val_len);
|
||||
};
|
||||
|
||||
#define __UCSI_CMD(_ctrl_, _cmd_) \
|
||||
{ \
|
||||
(_ctrl_).raw_cmd = 0; \
|
||||
(_ctrl_).cmd.cmd = _cmd_; \
|
||||
}
|
||||
struct ucsi *ucsi_create(struct device *dev, const struct ucsi_operations *ops);
|
||||
void ucsi_destroy(struct ucsi *ucsi);
|
||||
int ucsi_register(struct ucsi *ucsi);
|
||||
void ucsi_unregister(struct ucsi *ucsi);
|
||||
void *ucsi_get_drvdata(struct ucsi *ucsi);
|
||||
void ucsi_set_drvdata(struct ucsi *ucsi, void *data);
|
||||
|
||||
/* Helper for preparing ucsi_control for CONNECTOR_RESET command. */
|
||||
#define UCSI_CMD_CONNECTOR_RESET(_ctrl_, _con_, _hard_) \
|
||||
{ \
|
||||
__UCSI_CMD(_ctrl_, UCSI_CONNECTOR_RESET) \
|
||||
(_ctrl_).con_rst.con_num = (_con_)->num; \
|
||||
(_ctrl_).con_rst.hard_reset = _hard_; \
|
||||
}
|
||||
void ucsi_connector_change(struct ucsi *ucsi, u8 num);
|
||||
|
||||
/* Helper for preparing ucsi_control for ACK_CC_CI command. */
|
||||
#define UCSI_CMD_ACK(_ctrl_, _ack_) \
|
||||
{ \
|
||||
__UCSI_CMD(_ctrl_, UCSI_ACK_CC_CI) \
|
||||
(_ctrl_).ack.cci_ack = ((_ack_) == UCSI_ACK_EVENT); \
|
||||
(_ctrl_).ack.cmd_ack = ((_ack_) == UCSI_ACK_CMD); \
|
||||
}
|
||||
|
||||
/* Helper for preparing ucsi_control for SET_NOTIFY_ENABLE command. */
|
||||
#define UCSI_CMD_SET_NTFY_ENABLE(_ctrl_, _ntfys_) \
|
||||
{ \
|
||||
__UCSI_CMD(_ctrl_, UCSI_SET_NOTIFICATION_ENABLE) \
|
||||
(_ctrl_).cmd.data = _ntfys_; \
|
||||
}
|
||||
|
||||
/* Helper for preparing ucsi_control for GET_CAPABILITY command. */
|
||||
#define UCSI_CMD_GET_CAPABILITY(_ctrl_) \
|
||||
{ \
|
||||
__UCSI_CMD(_ctrl_, UCSI_GET_CAPABILITY) \
|
||||
}
|
||||
|
||||
/* Helper for preparing ucsi_control for GET_CONNECTOR_CAPABILITY command. */
|
||||
#define UCSI_CMD_GET_CONNECTOR_CAPABILITY(_ctrl_, _con_) \
|
||||
{ \
|
||||
__UCSI_CMD(_ctrl_, UCSI_GET_CONNECTOR_CAPABILITY) \
|
||||
(_ctrl_).cmd.data = _con_; \
|
||||
}
|
||||
|
||||
/* Helper for preparing ucsi_control for GET_ALTERNATE_MODES command. */
|
||||
#define UCSI_CMD_GET_ALTERNATE_MODES(_ctrl_, _r_, _con_num_, _o_, _num_)\
|
||||
{ \
|
||||
__UCSI_CMD((_ctrl_), UCSI_GET_ALTERNATE_MODES) \
|
||||
_ctrl_.alt.recipient = (_r_); \
|
||||
_ctrl_.alt.con_num = (_con_num_); \
|
||||
_ctrl_.alt.offset = (_o_); \
|
||||
_ctrl_.alt.num_altmodes = (_num_) - 1; \
|
||||
}
|
||||
|
||||
/* Helper for preparing ucsi_control for GET_CAM_SUPPORTED command. */
|
||||
#define UCSI_CMD_GET_CAM_SUPPORTED(_ctrl_, _con_) \
|
||||
{ \
|
||||
__UCSI_CMD((_ctrl_), UCSI_GET_CAM_SUPPORTED) \
|
||||
_ctrl_.cmd.data = (_con_); \
|
||||
}
|
||||
|
||||
/* Helper for preparing ucsi_control for GET_CAM_SUPPORTED command. */
|
||||
#define UCSI_CMD_GET_CURRENT_CAM(_ctrl_, _con_) \
|
||||
{ \
|
||||
__UCSI_CMD((_ctrl_), UCSI_GET_CURRENT_CAM) \
|
||||
_ctrl_.cmd.data = (_con_); \
|
||||
}
|
||||
|
||||
/* Helper for preparing ucsi_control for GET_CONNECTOR_STATUS command. */
|
||||
#define UCSI_CMD_GET_CONNECTOR_STATUS(_ctrl_, _con_) \
|
||||
{ \
|
||||
__UCSI_CMD(_ctrl_, UCSI_GET_CONNECTOR_STATUS) \
|
||||
(_ctrl_).cmd.data = _con_; \
|
||||
}
|
||||
|
||||
#define __UCSI_ROLE(_ctrl_, _cmd_, _con_num_) \
|
||||
{ \
|
||||
__UCSI_CMD(_ctrl_, _cmd_) \
|
||||
(_ctrl_).uor.con_num = _con_num_; \
|
||||
(_ctrl_).uor.role = UCSI_UOR_ROLE_DRP; \
|
||||
}
|
||||
|
||||
/* Helper for preparing ucsi_control for SET_UOR command. */
|
||||
#define UCSI_CMD_SET_UOR(_ctrl_, _con_, _role_) \
|
||||
{ \
|
||||
__UCSI_ROLE(_ctrl_, UCSI_SET_UOR, (_con_)->num) \
|
||||
(_ctrl_).uor.role |= (_role_) == TYPEC_HOST ? UCSI_UOR_ROLE_DFP : \
|
||||
UCSI_UOR_ROLE_UFP; \
|
||||
}
|
||||
|
||||
/* Helper for preparing ucsi_control for SET_PDR command. */
|
||||
#define UCSI_CMD_SET_PDR(_ctrl_, _con_, _role_) \
|
||||
{ \
|
||||
__UCSI_ROLE(_ctrl_, UCSI_SET_PDR, (_con_)->num) \
|
||||
(_ctrl_).uor.role |= (_role_) == TYPEC_SOURCE ? UCSI_UOR_ROLE_DFP : \
|
||||
UCSI_UOR_ROLE_UFP; \
|
||||
}
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
/* Commands */
|
||||
#define UCSI_PPM_RESET 0x01
|
||||
|
|
@ -203,24 +81,49 @@ struct ucsi_control {
|
|||
#define UCSI_GET_CONNECTOR_STATUS 0x12
|
||||
#define UCSI_GET_ERROR_STATUS 0x13
|
||||
|
||||
/* ACK_CC_CI commands */
|
||||
#define UCSI_ACK_EVENT 1
|
||||
#define UCSI_ACK_CMD 2
|
||||
#define UCSI_CONNECTOR_NUMBER(_num_) ((u64)(_num_) << 16)
|
||||
|
||||
/* Bits for SET_NOTIFICATION_ENABLE command */
|
||||
#define UCSI_ENABLE_NTFY_CMD_COMPLETE BIT(0)
|
||||
#define UCSI_ENABLE_NTFY_EXT_PWR_SRC_CHANGE BIT(1)
|
||||
#define UCSI_ENABLE_NTFY_PWR_OPMODE_CHANGE BIT(2)
|
||||
#define UCSI_ENABLE_NTFY_CAP_CHANGE BIT(5)
|
||||
#define UCSI_ENABLE_NTFY_PWR_LEVEL_CHANGE BIT(6)
|
||||
#define UCSI_ENABLE_NTFY_PD_RESET_COMPLETE BIT(7)
|
||||
#define UCSI_ENABLE_NTFY_CAM_CHANGE BIT(8)
|
||||
#define UCSI_ENABLE_NTFY_BAT_STATUS_CHANGE BIT(9)
|
||||
#define UCSI_ENABLE_NTFY_PARTNER_CHANGE BIT(11)
|
||||
#define UCSI_ENABLE_NTFY_PWR_DIR_CHANGE BIT(12)
|
||||
#define UCSI_ENABLE_NTFY_CONNECTOR_CHANGE BIT(14)
|
||||
#define UCSI_ENABLE_NTFY_ERROR BIT(15)
|
||||
#define UCSI_ENABLE_NTFY_ALL 0xdbe7
|
||||
/* CONNECTOR_RESET command bits */
|
||||
#define UCSI_CONNECTOR_RESET_HARD BIT(23) /* Deprecated in v1.1 */
|
||||
|
||||
/* ACK_CC_CI bits */
|
||||
#define UCSI_ACK_CONNECTOR_CHANGE BIT(16)
|
||||
#define UCSI_ACK_COMMAND_COMPLETE BIT(17)
|
||||
|
||||
/* SET_NOTIFICATION_ENABLE command bits */
|
||||
#define UCSI_ENABLE_NTFY_CMD_COMPLETE BIT(16)
|
||||
#define UCSI_ENABLE_NTFY_EXT_PWR_SRC_CHANGE BIT(17)
|
||||
#define UCSI_ENABLE_NTFY_PWR_OPMODE_CHANGE BIT(18)
|
||||
#define UCSI_ENABLE_NTFY_CAP_CHANGE BIT(19)
|
||||
#define UCSI_ENABLE_NTFY_PWR_LEVEL_CHANGE BIT(20)
|
||||
#define UCSI_ENABLE_NTFY_PD_RESET_COMPLETE BIT(21)
|
||||
#define UCSI_ENABLE_NTFY_CAM_CHANGE BIT(22)
|
||||
#define UCSI_ENABLE_NTFY_BAT_STATUS_CHANGE BIT(23)
|
||||
#define UCSI_ENABLE_NTFY_PARTNER_CHANGE BIT(24)
|
||||
#define UCSI_ENABLE_NTFY_PWR_DIR_CHANGE BIT(25)
|
||||
#define UCSI_ENABLE_NTFY_CONNECTOR_CHANGE BIT(26)
|
||||
#define UCSI_ENABLE_NTFY_ERROR BIT(27)
|
||||
#define UCSI_ENABLE_NTFY_ALL 0xdbe70000
|
||||
|
||||
/* SET_UOR command bits */
|
||||
#define UCSI_SET_UOR_ROLE(_r_) (((_r_) == TYPEC_HOST ? 1 : 2) << 23)
|
||||
#define UCSI_SET_UOR_ACCEPT_ROLE_SWAPS BIT(25)
|
||||
|
||||
/* SET_PDF command bits */
|
||||
#define UCSI_SET_PDR_ROLE(_r_) (((_r_) == TYPEC_SOURCE ? 1 : 2) << 23)
|
||||
#define UCSI_SET_PDR_ACCEPT_ROLE_SWAPS BIT(25)
|
||||
|
||||
/* GET_ALTERNATE_MODES command bits */
|
||||
#define UCSI_GET_ALTMODE_RECIPIENT(_r_) ((u64)(_r_) << 16)
|
||||
#define UCSI_RECIPIENT_CON 0
|
||||
#define UCSI_RECIPIENT_SOP 1
|
||||
#define UCSI_RECIPIENT_SOP_P 2
|
||||
#define UCSI_RECIPIENT_SOP_PP 3
|
||||
#define UCSI_GET_ALTMODE_CONNECTOR_NUMBER(_r_) ((u64)(_r_) << 24)
|
||||
#define UCSI_GET_ALTMODE_OFFSET(_r_) ((u64)(_r_) << 32)
|
||||
#define UCSI_GET_ALTMODE_NUM_ALTMODES(_r_) ((u64)(_r_) << 40)
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
/* Error information returned by PPM in response to GET_ERROR_STATUS command. */
|
||||
#define UCSI_ERROR_UNREGONIZED_CMD BIT(0)
|
||||
|
|
@ -230,6 +133,12 @@ struct ucsi_control {
|
|||
#define UCSI_ERROR_CC_COMMUNICATION_ERR BIT(4)
|
||||
#define UCSI_ERROR_DEAD_BATTERY BIT(5)
|
||||
#define UCSI_ERROR_CONTRACT_NEGOTIATION_FAIL BIT(6)
|
||||
#define UCSI_ERROR_OVERCURRENT BIT(7)
|
||||
#define UCSI_ERROR_UNDEFINED BIT(8)
|
||||
#define UCSI_ERROR_PARTNER_REJECTED_SWAP BIT(9)
|
||||
#define UCSI_ERROR_HARD_RESET BIT(10)
|
||||
#define UCSI_ERROR_PPM_POLICY_CONFLICT BIT(11)
|
||||
#define UCSI_ERROR_SWAP_REJECTED BIT(12)
|
||||
|
||||
/* Data structure filled by PPM in response to GET_CAPABILITY command. */
|
||||
struct ucsi_capability {
|
||||
|
|
@ -241,8 +150,8 @@ struct ucsi_capability {
|
|||
#define UCSI_CAP_ATTR_POWER_AC_SUPPLY BIT(8)
|
||||
#define UCSI_CAP_ATTR_POWER_OTHER BIT(10)
|
||||
#define UCSI_CAP_ATTR_POWER_VBUS BIT(14)
|
||||
u32 num_connectors:8;
|
||||
u32 features:24;
|
||||
u8 num_connectors;
|
||||
u8 features;
|
||||
#define UCSI_CAP_SET_UOM BIT(0)
|
||||
#define UCSI_CAP_SET_PDM BIT(1)
|
||||
#define UCSI_CAP_ALT_MODE_DETAILS BIT(2)
|
||||
|
|
@ -251,8 +160,9 @@ struct ucsi_capability {
|
|||
#define UCSI_CAP_CABLE_DETAILS BIT(5)
|
||||
#define UCSI_CAP_EXT_SUPPLY_NOTIFICATIONS BIT(6)
|
||||
#define UCSI_CAP_PD_RESET BIT(7)
|
||||
u16 reserved_1;
|
||||
u8 num_alt_modes;
|
||||
u8 reserved;
|
||||
u8 reserved_2;
|
||||
u16 bc_version;
|
||||
u16 pd_version;
|
||||
u16 typec_version;
|
||||
|
|
@ -269,9 +179,9 @@ struct ucsi_connector_capability {
|
|||
#define UCSI_CONCAP_OPMODE_USB2 BIT(5)
|
||||
#define UCSI_CONCAP_OPMODE_USB3 BIT(6)
|
||||
#define UCSI_CONCAP_OPMODE_ALT_MODE BIT(7)
|
||||
u8 provider:1;
|
||||
u8 consumer:1;
|
||||
u8:6; /* reserved */
|
||||
u8 flags;
|
||||
#define UCSI_CONCAP_FLAG_PROVIDER BIT(0)
|
||||
#define UCSI_CONCAP_FLAG_CONSUMER BIT(1)
|
||||
} __packed;
|
||||
|
||||
struct ucsi_altmode {
|
||||
|
|
@ -283,18 +193,17 @@ struct ucsi_altmode {
|
|||
struct ucsi_cable_property {
|
||||
u16 speed_supported;
|
||||
u8 current_capability;
|
||||
u8 vbus_in_cable:1;
|
||||
u8 active_cable:1;
|
||||
u8 directionality:1;
|
||||
u8 plug_type:2;
|
||||
#define UCSI_CABLE_PROPERTY_PLUG_TYPE_A 0
|
||||
#define UCSI_CABLE_PROPERTY_PLUG_TYPE_B 1
|
||||
#define UCSI_CABLE_PROPERTY_PLUG_TYPE_C 2
|
||||
#define UCSI_CABLE_PROPERTY_PLUG_OTHER 3
|
||||
u8 mode_support:1;
|
||||
u8:2; /* reserved */
|
||||
u8 latency:4;
|
||||
u8:4; /* reserved */
|
||||
u8 flags;
|
||||
#define UCSI_CABLE_PROP_FLAG_VBUS_IN_CABLE BIT(0)
|
||||
#define UCSI_CABLE_PROP_FLAG_ACTIVE_CABLE BIT(1)
|
||||
#define UCSI_CABLE_PROP_FLAG_DIRECTIONALITY BIT(2)
|
||||
#define UCSI_CABLE_PROP_FLAG_PLUG_TYPE(_f_) ((_f_) & GENMASK(3, 0))
|
||||
#define UCSI_CABLE_PROPERTY_PLUG_TYPE_A 0
|
||||
#define UCSI_CABLE_PROPERTY_PLUG_TYPE_B 1
|
||||
#define UCSI_CABLE_PROPERTY_PLUG_TYPE_C 2
|
||||
#define UCSI_CABLE_PROPERTY_PLUG_OTHER 3
|
||||
#define UCSI_CABLE_PROP_MODE_SUPPORT BIT(5)
|
||||
u8 latency;
|
||||
} __packed;
|
||||
|
||||
/* Data structure filled by PPM in response to GET_CONNECTOR_STATUS command. */
|
||||
|
|
@ -311,83 +220,47 @@ struct ucsi_connector_status {
|
|||
#define UCSI_CONSTAT_POWER_DIR_CHANGE BIT(12)
|
||||
#define UCSI_CONSTAT_CONNECT_CHANGE BIT(14)
|
||||
#define UCSI_CONSTAT_ERROR BIT(15)
|
||||
u16 pwr_op_mode:3;
|
||||
#define UCSI_CONSTAT_PWR_OPMODE_NONE 0
|
||||
#define UCSI_CONSTAT_PWR_OPMODE_DEFAULT 1
|
||||
#define UCSI_CONSTAT_PWR_OPMODE_BC 2
|
||||
#define UCSI_CONSTAT_PWR_OPMODE_PD 3
|
||||
#define UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5 4
|
||||
#define UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0 5
|
||||
u16 connected:1;
|
||||
u16 pwr_dir:1;
|
||||
u16 partner_flags:8;
|
||||
#define UCSI_CONSTAT_PARTNER_FLAG_USB BIT(0)
|
||||
#define UCSI_CONSTAT_PARTNER_FLAG_ALT_MODE BIT(1)
|
||||
u16 partner_type:3;
|
||||
#define UCSI_CONSTAT_PARTNER_TYPE_DFP 1
|
||||
#define UCSI_CONSTAT_PARTNER_TYPE_UFP 2
|
||||
#define UCSI_CONSTAT_PARTNER_TYPE_CABLE 3 /* Powered Cable */
|
||||
#define UCSI_CONSTAT_PARTNER_TYPE_CABLE_AND_UFP 4 /* Powered Cable */
|
||||
#define UCSI_CONSTAT_PARTNER_TYPE_DEBUG 5
|
||||
#define UCSI_CONSTAT_PARTNER_TYPE_AUDIO 6
|
||||
u16 flags;
|
||||
#define UCSI_CONSTAT_PWR_OPMODE(_f_) ((_f_) & GENMASK(2, 0))
|
||||
#define UCSI_CONSTAT_PWR_OPMODE_NONE 0
|
||||
#define UCSI_CONSTAT_PWR_OPMODE_DEFAULT 1
|
||||
#define UCSI_CONSTAT_PWR_OPMODE_BC 2
|
||||
#define UCSI_CONSTAT_PWR_OPMODE_PD 3
|
||||
#define UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5 4
|
||||
#define UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0 5
|
||||
#define UCSI_CONSTAT_CONNECTED BIT(3)
|
||||
#define UCSI_CONSTAT_PWR_DIR BIT(4)
|
||||
#define UCSI_CONSTAT_PARTNER_FLAGS(_f_) (((_f_) & GENMASK(12, 5)) >> 5)
|
||||
#define UCSI_CONSTAT_PARTNER_FLAG_USB 1
|
||||
#define UCSI_CONSTAT_PARTNER_FLAG_ALT_MODE 2
|
||||
#define UCSI_CONSTAT_PARTNER_TYPE(_f_) (((_f_) & GENMASK(15, 13)) >> 13)
|
||||
#define UCSI_CONSTAT_PARTNER_TYPE_DFP 1
|
||||
#define UCSI_CONSTAT_PARTNER_TYPE_UFP 2
|
||||
#define UCSI_CONSTAT_PARTNER_TYPE_CABLE 3 /* Powered Cable */
|
||||
#define UCSI_CONSTAT_PARTNER_TYPE_CABLE_AND_UFP 4 /* Powered Cable */
|
||||
#define UCSI_CONSTAT_PARTNER_TYPE_DEBUG 5
|
||||
#define UCSI_CONSTAT_PARTNER_TYPE_AUDIO 6
|
||||
u32 request_data_obj;
|
||||
u8 bc_status:2;
|
||||
#define UCSI_CONSTAT_BC_NOT_CHARGING 0
|
||||
#define UCSI_CONSTAT_BC_NOMINAL_CHARGING 1
|
||||
#define UCSI_CONSTAT_BC_SLOW_CHARGING 2
|
||||
#define UCSI_CONSTAT_BC_TRICKLE_CHARGING 3
|
||||
u8 provider_cap_limit_reason:4;
|
||||
#define UCSI_CONSTAT_CAP_PWR_LOWERED 0
|
||||
#define UCSI_CONSTAT_CAP_PWR_BUDGET_LIMIT 1
|
||||
u8:2; /* reserved */
|
||||
u8 pwr_status;
|
||||
#define UCSI_CONSTAT_BC_STATUS(_p_) ((_p_) & GENMASK(2, 0))
|
||||
#define UCSI_CONSTAT_BC_NOT_CHARGING 0
|
||||
#define UCSI_CONSTAT_BC_NOMINAL_CHARGING 1
|
||||
#define UCSI_CONSTAT_BC_SLOW_CHARGING 2
|
||||
#define UCSI_CONSTAT_BC_TRICKLE_CHARGING 3
|
||||
#define UCSI_CONSTAT_PROVIDER_CAP_LIMIT(_p_) (((_p_) & GENMASK(6, 3)) >> 3)
|
||||
#define UCSI_CONSTAT_CAP_PWR_LOWERED 0
|
||||
#define UCSI_CONSTAT_CAP_PWR_BUDGET_LIMIT 1
|
||||
} __packed;
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
struct ucsi;
|
||||
|
||||
struct ucsi_data {
|
||||
u16 version;
|
||||
u16 reserved;
|
||||
union {
|
||||
u32 raw_cci;
|
||||
struct ucsi_cci cci;
|
||||
};
|
||||
struct ucsi_control ctrl;
|
||||
u32 message_in[4];
|
||||
u32 message_out[4];
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* struct ucsi_ppm - Interface to UCSI Platform Policy Manager
|
||||
* @data: memory location to the UCSI data structures
|
||||
* @cmd: UCSI command execution routine
|
||||
* @sync: Refresh UCSI mailbox (the data structures)
|
||||
*/
|
||||
struct ucsi_ppm {
|
||||
struct ucsi_data *data;
|
||||
int (*cmd)(struct ucsi_ppm *, struct ucsi_control *);
|
||||
int (*sync)(struct ucsi_ppm *);
|
||||
};
|
||||
|
||||
struct ucsi *ucsi_register_ppm(struct device *dev, struct ucsi_ppm *ppm);
|
||||
void ucsi_unregister_ppm(struct ucsi *ucsi);
|
||||
void ucsi_notify(struct ucsi *ucsi);
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
enum ucsi_status {
|
||||
UCSI_IDLE = 0,
|
||||
UCSI_BUSY,
|
||||
UCSI_ERROR,
|
||||
};
|
||||
|
||||
struct ucsi {
|
||||
u16 version;
|
||||
struct device *dev;
|
||||
struct ucsi_ppm *ppm;
|
||||
struct driver_data *driver_data;
|
||||
|
||||
const struct ucsi_operations *ops;
|
||||
|
||||
enum ucsi_status status;
|
||||
struct completion complete;
|
||||
struct ucsi_capability cap;
|
||||
struct ucsi_connector *connector;
|
||||
|
||||
|
|
@ -426,7 +299,7 @@ struct ucsi_connector {
|
|||
struct ucsi_connector_capability cap;
|
||||
};
|
||||
|
||||
int ucsi_send_command(struct ucsi *ucsi, struct ucsi_control *ctrl,
|
||||
int ucsi_send_command(struct ucsi *ucsi, u64 command,
|
||||
void *retval, size_t size);
|
||||
|
||||
void ucsi_altmode_update_active(struct ucsi_connector *con);
|
||||
|
|
|
|||
|
|
@ -19,7 +19,9 @@
|
|||
struct ucsi_acpi {
|
||||
struct device *dev;
|
||||
struct ucsi *ucsi;
|
||||
struct ucsi_ppm ppm;
|
||||
void __iomem *base;
|
||||
struct completion complete;
|
||||
unsigned long flags;
|
||||
guid_t guid;
|
||||
};
|
||||
|
||||
|
|
@ -39,27 +41,73 @@ static int ucsi_acpi_dsm(struct ucsi_acpi *ua, int func)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int ucsi_acpi_cmd(struct ucsi_ppm *ppm, struct ucsi_control *ctrl)
|
||||
static int ucsi_acpi_read(struct ucsi *ucsi, unsigned int offset,
|
||||
void *val, size_t val_len)
|
||||
{
|
||||
struct ucsi_acpi *ua = container_of(ppm, struct ucsi_acpi, ppm);
|
||||
struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
|
||||
int ret;
|
||||
|
||||
ppm->data->ctrl.raw_cmd = ctrl->raw_cmd;
|
||||
ret = ucsi_acpi_dsm(ua, UCSI_DSM_FUNC_READ);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
memcpy(val, (const void __force *)(ua->base + offset), val_len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ucsi_acpi_async_write(struct ucsi *ucsi, unsigned int offset,
|
||||
const void *val, size_t val_len)
|
||||
{
|
||||
struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
|
||||
|
||||
memcpy((void __force *)(ua->base + offset), val, val_len);
|
||||
|
||||
return ucsi_acpi_dsm(ua, UCSI_DSM_FUNC_WRITE);
|
||||
}
|
||||
|
||||
static int ucsi_acpi_sync(struct ucsi_ppm *ppm)
|
||||
static int ucsi_acpi_sync_write(struct ucsi *ucsi, unsigned int offset,
|
||||
const void *val, size_t val_len)
|
||||
{
|
||||
struct ucsi_acpi *ua = container_of(ppm, struct ucsi_acpi, ppm);
|
||||
struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
|
||||
int ret;
|
||||
|
||||
return ucsi_acpi_dsm(ua, UCSI_DSM_FUNC_READ);
|
||||
set_bit(COMMAND_PENDING, &ua->flags);
|
||||
|
||||
ret = ucsi_acpi_async_write(ucsi, offset, val, val_len);
|
||||
if (ret)
|
||||
goto out_clear_bit;
|
||||
|
||||
if (!wait_for_completion_timeout(&ua->complete, msecs_to_jiffies(5000)))
|
||||
ret = -ETIMEDOUT;
|
||||
|
||||
out_clear_bit:
|
||||
clear_bit(COMMAND_PENDING, &ua->flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct ucsi_operations ucsi_acpi_ops = {
|
||||
.read = ucsi_acpi_read,
|
||||
.sync_write = ucsi_acpi_sync_write,
|
||||
.async_write = ucsi_acpi_async_write
|
||||
};
|
||||
|
||||
static void ucsi_acpi_notify(acpi_handle handle, u32 event, void *data)
|
||||
{
|
||||
struct ucsi_acpi *ua = data;
|
||||
u32 cci;
|
||||
int ret;
|
||||
|
||||
ucsi_notify(ua->ucsi);
|
||||
ret = ucsi_acpi_read(ua->ucsi, UCSI_CCI, &cci, sizeof(cci));
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
if (test_bit(COMMAND_PENDING, &ua->flags) &&
|
||||
cci & (UCSI_CCI_ACK_COMPLETE | UCSI_CCI_COMMAND_COMPLETE))
|
||||
complete(&ua->complete);
|
||||
else if (UCSI_CCI_CONNECTOR(cci))
|
||||
ucsi_connector_change(ua->ucsi, UCSI_CCI_CONNECTOR(cci));
|
||||
}
|
||||
|
||||
static int ucsi_acpi_probe(struct platform_device *pdev)
|
||||
|
|
@ -90,35 +138,39 @@ static int ucsi_acpi_probe(struct platform_device *pdev)
|
|||
* it can not be requested here, and we can not use
|
||||
* devm_ioremap_resource().
|
||||
*/
|
||||
ua->ppm.data = devm_ioremap(&pdev->dev, res->start, resource_size(res));
|
||||
if (!ua->ppm.data)
|
||||
ua->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
|
||||
if (!ua->base)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!ua->ppm.data->version)
|
||||
return -ENODEV;
|
||||
|
||||
ret = guid_parse(UCSI_DSM_UUID, &ua->guid);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ua->ppm.cmd = ucsi_acpi_cmd;
|
||||
ua->ppm.sync = ucsi_acpi_sync;
|
||||
init_completion(&ua->complete);
|
||||
ua->dev = &pdev->dev;
|
||||
|
||||
ua->ucsi = ucsi_create(&pdev->dev, &ucsi_acpi_ops);
|
||||
if (IS_ERR(ua->ucsi))
|
||||
return PTR_ERR(ua->ucsi);
|
||||
|
||||
ucsi_set_drvdata(ua->ucsi, ua);
|
||||
|
||||
status = acpi_install_notify_handler(ACPI_HANDLE(&pdev->dev),
|
||||
ACPI_DEVICE_NOTIFY,
|
||||
ucsi_acpi_notify, ua);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
dev_err(&pdev->dev, "failed to install notify handler\n");
|
||||
ucsi_destroy(ua->ucsi);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ua->ucsi = ucsi_register_ppm(&pdev->dev, &ua->ppm);
|
||||
if (IS_ERR(ua->ucsi)) {
|
||||
ret = ucsi_register(ua->ucsi);
|
||||
if (ret) {
|
||||
acpi_remove_notify_handler(ACPI_HANDLE(&pdev->dev),
|
||||
ACPI_DEVICE_NOTIFY,
|
||||
ucsi_acpi_notify);
|
||||
return PTR_ERR(ua->ucsi);
|
||||
ucsi_destroy(ua->ucsi);
|
||||
return ret;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, ua);
|
||||
|
|
@ -130,7 +182,8 @@ static int ucsi_acpi_remove(struct platform_device *pdev)
|
|||
{
|
||||
struct ucsi_acpi *ua = platform_get_drvdata(pdev);
|
||||
|
||||
ucsi_unregister_ppm(ua->ucsi);
|
||||
ucsi_unregister(ua->ucsi);
|
||||
ucsi_destroy(ua->ucsi);
|
||||
|
||||
acpi_remove_notify_handler(ACPI_HANDLE(&pdev->dev), ACPI_DEVICE_NOTIFY,
|
||||
ucsi_acpi_notify);
|
||||
|
|
|
|||
|
|
@ -176,8 +176,8 @@ struct ccg_resp {
|
|||
struct ucsi_ccg {
|
||||
struct device *dev;
|
||||
struct ucsi *ucsi;
|
||||
struct ucsi_ppm ppm;
|
||||
struct i2c_client *client;
|
||||
|
||||
struct ccg_dev_info info;
|
||||
/* version info for boot, primary and secondary */
|
||||
struct version_info version[FW2 + 1];
|
||||
|
|
@ -196,6 +196,8 @@ struct ucsi_ccg {
|
|||
/* fw build with vendor information */
|
||||
u16 fw_build;
|
||||
struct work_struct pm_work;
|
||||
|
||||
struct completion complete;
|
||||
};
|
||||
|
||||
static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len)
|
||||
|
|
@ -243,7 +245,7 @@ static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int ccg_write(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len)
|
||||
static int ccg_write(struct ucsi_ccg *uc, u16 rab, const u8 *data, u32 len)
|
||||
{
|
||||
struct i2c_client *client = uc->client;
|
||||
unsigned char *buf;
|
||||
|
|
@ -317,88 +319,85 @@ static int ucsi_ccg_init(struct ucsi_ccg *uc)
|
|||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int ucsi_ccg_send_data(struct ucsi_ccg *uc)
|
||||
static int ucsi_ccg_read(struct ucsi *ucsi, unsigned int offset,
|
||||
void *val, size_t val_len)
|
||||
{
|
||||
u8 *ppm = (u8 *)uc->ppm.data;
|
||||
int status;
|
||||
u16 rab;
|
||||
u16 reg = CCGX_RAB_UCSI_DATA_BLOCK(offset);
|
||||
|
||||
rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, message_out));
|
||||
status = ccg_write(uc, rab, ppm +
|
||||
offsetof(struct ucsi_data, message_out),
|
||||
sizeof(uc->ppm.data->message_out));
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, ctrl));
|
||||
return ccg_write(uc, rab, ppm + offsetof(struct ucsi_data, ctrl),
|
||||
sizeof(uc->ppm.data->ctrl));
|
||||
return ccg_read(ucsi_get_drvdata(ucsi), reg, val, val_len);
|
||||
}
|
||||
|
||||
static int ucsi_ccg_recv_data(struct ucsi_ccg *uc)
|
||||
static int ucsi_ccg_async_write(struct ucsi *ucsi, unsigned int offset,
|
||||
const void *val, size_t val_len)
|
||||
{
|
||||
u8 *ppm = (u8 *)uc->ppm.data;
|
||||
int status;
|
||||
u16 rab;
|
||||
u16 reg = CCGX_RAB_UCSI_DATA_BLOCK(offset);
|
||||
|
||||
rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, cci));
|
||||
status = ccg_read(uc, rab, ppm + offsetof(struct ucsi_data, cci),
|
||||
sizeof(uc->ppm.data->cci));
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, message_in));
|
||||
return ccg_read(uc, rab, ppm + offsetof(struct ucsi_data, message_in),
|
||||
sizeof(uc->ppm.data->message_in));
|
||||
return ccg_write(ucsi_get_drvdata(ucsi), reg, val, val_len);
|
||||
}
|
||||
|
||||
static int ucsi_ccg_ack_interrupt(struct ucsi_ccg *uc)
|
||||
static int ucsi_ccg_sync_write(struct ucsi *ucsi, unsigned int offset,
|
||||
const void *val, size_t val_len)
|
||||
{
|
||||
int status;
|
||||
unsigned char data;
|
||||
struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi);
|
||||
int ret;
|
||||
|
||||
status = ccg_read(uc, CCGX_RAB_INTR_REG, &data, sizeof(data));
|
||||
if (status < 0)
|
||||
return status;
|
||||
mutex_lock(&uc->lock);
|
||||
pm_runtime_get_sync(uc->dev);
|
||||
set_bit(DEV_CMD_PENDING, &uc->flags);
|
||||
|
||||
return ccg_write(uc, CCGX_RAB_INTR_REG, &data, sizeof(data));
|
||||
ret = ucsi_ccg_async_write(ucsi, offset, val, val_len);
|
||||
if (ret)
|
||||
goto err_clear_bit;
|
||||
|
||||
if (!wait_for_completion_timeout(&uc->complete, msecs_to_jiffies(5000)))
|
||||
ret = -ETIMEDOUT;
|
||||
|
||||
err_clear_bit:
|
||||
clear_bit(DEV_CMD_PENDING, &uc->flags);
|
||||
pm_runtime_put_sync(uc->dev);
|
||||
mutex_unlock(&uc->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ucsi_ccg_sync(struct ucsi_ppm *ppm)
|
||||
{
|
||||
struct ucsi_ccg *uc = container_of(ppm, struct ucsi_ccg, ppm);
|
||||
int status;
|
||||
|
||||
status = ucsi_ccg_recv_data(uc);
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
/* ack interrupt to allow next command to run */
|
||||
return ucsi_ccg_ack_interrupt(uc);
|
||||
}
|
||||
|
||||
static int ucsi_ccg_cmd(struct ucsi_ppm *ppm, struct ucsi_control *ctrl)
|
||||
{
|
||||
struct ucsi_ccg *uc = container_of(ppm, struct ucsi_ccg, ppm);
|
||||
|
||||
ppm->data->ctrl.raw_cmd = ctrl->raw_cmd;
|
||||
return ucsi_ccg_send_data(uc);
|
||||
}
|
||||
static const struct ucsi_operations ucsi_ccg_ops = {
|
||||
.read = ucsi_ccg_read,
|
||||
.sync_write = ucsi_ccg_sync_write,
|
||||
.async_write = ucsi_ccg_async_write
|
||||
};
|
||||
|
||||
static irqreturn_t ccg_irq_handler(int irq, void *data)
|
||||
{
|
||||
u16 reg = CCGX_RAB_UCSI_DATA_BLOCK(UCSI_CCI);
|
||||
struct ucsi_ccg *uc = data;
|
||||
u8 intr_reg;
|
||||
u32 cci;
|
||||
int ret;
|
||||
|
||||
ucsi_notify(uc->ucsi);
|
||||
ret = ccg_read(uc, CCGX_RAB_INTR_REG, &intr_reg, sizeof(intr_reg));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ccg_read(uc, reg, (void *)&cci, sizeof(cci));
|
||||
if (ret)
|
||||
goto err_clear_irq;
|
||||
|
||||
if (UCSI_CCI_CONNECTOR(cci))
|
||||
ucsi_connector_change(uc->ucsi, UCSI_CCI_CONNECTOR(cci));
|
||||
|
||||
if (test_bit(DEV_CMD_PENDING, &uc->flags) &&
|
||||
cci & (UCSI_CCI_ACK_COMPLETE | UCSI_CCI_COMMAND_COMPLETE))
|
||||
complete(&uc->complete);
|
||||
|
||||
err_clear_irq:
|
||||
ccg_write(uc, CCGX_RAB_INTR_REG, &intr_reg, sizeof(intr_reg));
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void ccg_pm_workaround_work(struct work_struct *pm_work)
|
||||
{
|
||||
struct ucsi_ccg *uc = container_of(pm_work, struct ucsi_ccg, pm_work);
|
||||
|
||||
ucsi_notify(uc->ucsi);
|
||||
ccg_irq_handler(0, container_of(pm_work, struct ucsi_ccg, pm_work));
|
||||
}
|
||||
|
||||
static int get_fw_info(struct ucsi_ccg *uc)
|
||||
|
|
@ -1027,10 +1026,10 @@ static int ccg_restart(struct ucsi_ccg *uc)
|
|||
return status;
|
||||
}
|
||||
|
||||
uc->ucsi = ucsi_register_ppm(dev, &uc->ppm);
|
||||
if (IS_ERR(uc->ucsi)) {
|
||||
dev_err(uc->dev, "ucsi_register_ppm failed\n");
|
||||
return PTR_ERR(uc->ucsi);
|
||||
status = ucsi_register(uc->ucsi);
|
||||
if (status) {
|
||||
dev_err(uc->dev, "failed to register the interface\n");
|
||||
return status;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
@ -1047,7 +1046,7 @@ static void ccg_update_firmware(struct work_struct *work)
|
|||
return;
|
||||
|
||||
if (flash_mode != FLASH_NOT_NEEDED) {
|
||||
ucsi_unregister_ppm(uc->ucsi);
|
||||
ucsi_unregister(uc->ucsi);
|
||||
free_irq(uc->irq, uc);
|
||||
|
||||
ccg_fw_update(uc, flash_mode);
|
||||
|
|
@ -1091,21 +1090,15 @@ static int ucsi_ccg_probe(struct i2c_client *client,
|
|||
struct device *dev = &client->dev;
|
||||
struct ucsi_ccg *uc;
|
||||
int status;
|
||||
u16 rab;
|
||||
|
||||
uc = devm_kzalloc(dev, sizeof(*uc), GFP_KERNEL);
|
||||
if (!uc)
|
||||
return -ENOMEM;
|
||||
|
||||
uc->ppm.data = devm_kzalloc(dev, sizeof(struct ucsi_data), GFP_KERNEL);
|
||||
if (!uc->ppm.data)
|
||||
return -ENOMEM;
|
||||
|
||||
uc->ppm.cmd = ucsi_ccg_cmd;
|
||||
uc->ppm.sync = ucsi_ccg_sync;
|
||||
uc->dev = dev;
|
||||
uc->client = client;
|
||||
mutex_init(&uc->lock);
|
||||
init_completion(&uc->complete);
|
||||
INIT_WORK(&uc->work, ccg_update_firmware);
|
||||
INIT_WORK(&uc->pm_work, ccg_pm_workaround_work);
|
||||
|
||||
|
|
@ -1133,30 +1126,25 @@ static int ucsi_ccg_probe(struct i2c_client *client,
|
|||
if (uc->info.mode & CCG_DEVINFO_PDPORTS_MASK)
|
||||
uc->port_num++;
|
||||
|
||||
uc->ucsi = ucsi_create(dev, &ucsi_ccg_ops);
|
||||
if (IS_ERR(uc->ucsi))
|
||||
return PTR_ERR(uc->ucsi);
|
||||
|
||||
ucsi_set_drvdata(uc->ucsi, uc);
|
||||
|
||||
status = request_threaded_irq(client->irq, NULL, ccg_irq_handler,
|
||||
IRQF_ONESHOT | IRQF_TRIGGER_HIGH,
|
||||
dev_name(dev), uc);
|
||||
if (status < 0) {
|
||||
dev_err(uc->dev, "request_threaded_irq failed - %d\n", status);
|
||||
return status;
|
||||
goto out_ucsi_destroy;
|
||||
}
|
||||
|
||||
uc->irq = client->irq;
|
||||
|
||||
uc->ucsi = ucsi_register_ppm(dev, &uc->ppm);
|
||||
if (IS_ERR(uc->ucsi)) {
|
||||
dev_err(uc->dev, "ucsi_register_ppm failed\n");
|
||||
return PTR_ERR(uc->ucsi);
|
||||
}
|
||||
|
||||
rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, version));
|
||||
status = ccg_read(uc, rab, (u8 *)(uc->ppm.data) +
|
||||
offsetof(struct ucsi_data, version),
|
||||
sizeof(uc->ppm.data->version));
|
||||
if (status < 0) {
|
||||
ucsi_unregister_ppm(uc->ucsi);
|
||||
return status;
|
||||
}
|
||||
status = ucsi_register(uc->ucsi);
|
||||
if (status)
|
||||
goto out_free_irq;
|
||||
|
||||
i2c_set_clientdata(client, uc);
|
||||
|
||||
|
|
@ -1167,6 +1155,13 @@ static int ucsi_ccg_probe(struct i2c_client *client,
|
|||
pm_runtime_idle(uc->dev);
|
||||
|
||||
return 0;
|
||||
|
||||
out_free_irq:
|
||||
free_irq(uc->irq, uc);
|
||||
out_ucsi_destroy:
|
||||
ucsi_destroy(uc->ucsi);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int ucsi_ccg_remove(struct i2c_client *client)
|
||||
|
|
@ -1175,8 +1170,9 @@ static int ucsi_ccg_remove(struct i2c_client *client)
|
|||
|
||||
cancel_work_sync(&uc->pm_work);
|
||||
cancel_work_sync(&uc->work);
|
||||
ucsi_unregister_ppm(uc->ucsi);
|
||||
pm_runtime_disable(uc->dev);
|
||||
ucsi_unregister(uc->ucsi);
|
||||
ucsi_destroy(uc->ucsi);
|
||||
free_irq(uc->irq, uc);
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
Loading…
Reference in a new issue