@@ -140,10 +140,7 @@ static int ucsi_exec_command(struct ucsi *ucsi, u64 cmd)
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;
+ cci = READ_ONCE(ucsi->cci);
if (cmd != UCSI_CANCEL && cci & UCSI_CCI_BUSY)
return ucsi_exec_command(ucsi, UCSI_CANCEL);
@@ -1037,9 +1034,10 @@ static int ucsi_reset_ppm(struct ucsi *ucsi)
goto out;
}
- ret = ucsi->ops->read(ucsi, UCSI_CCI, &cci, sizeof(cci));
+ ret = ucsi->ops->poll_cci(ucsi);
if (ret)
goto out;
+ cci = READ_ONCE(ucsi->cci);
/* If the PPM is still doing something else, reset it again. */
if (cci & ~UCSI_CCI_RESET_COMPLETE) {
@@ -1550,7 +1548,8 @@ 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)
+ if (!ops || !ops->poll_cci || !ops->read || !ops->sync_write ||
+ !ops->async_write)
return ERR_PTR(-EINVAL);
ucsi = kzalloc(sizeof(*ucsi), GFP_KERNEL);
@@ -55,6 +55,7 @@ struct dentry;
/**
* struct ucsi_operations - UCSI I/O operations
+ * @poll_cci: Update the cached CCI value from hardware. Required for reset.
* @read: Read operation
* @sync_write: Blocking write operation
* @async_write: Non-blocking write operation
@@ -65,6 +66,7 @@ struct dentry;
* return immediately after sending the data to the PPM.
*/
struct ucsi_operations {
+ int (*poll_cci)(struct ucsi *ucsi);
int (*read)(struct ucsi *ucsi, unsigned int offset,
void *val, size_t val_len);
int (*sync_write)(struct ucsi *ucsi, unsigned int offset,
@@ -371,6 +373,9 @@ struct ucsi {
/* The latest "Notification Enable" bits (SET_NOTIFICATION_ENABLE) */
u64 ntfy;
+ /* The current value of the CCI field. Synced by notifications. */
+ u32 cci;
+
/* PPM communication flags */
unsigned long flags;
#define EVENT_PENDING 0
@@ -24,7 +24,6 @@ struct ucsi_acpi {
struct completion complete;
unsigned long flags;
guid_t guid;
- u64 cmd;
bool dell_quirk_probed;
bool dell_quirk_active;
};
@@ -45,8 +44,7 @@ static int ucsi_acpi_dsm(struct ucsi_acpi *ua, int func)
return 0;
}
-static int ucsi_acpi_read(struct ucsi *ucsi, unsigned int offset,
- void *val, size_t val_len)
+static int ucsi_acpi_poll_cci(struct ucsi *ucsi)
{
struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
int ret;
@@ -55,6 +53,16 @@ static int ucsi_acpi_read(struct ucsi *ucsi, unsigned int offset,
if (ret)
return ret;
+ WRITE_ONCE(ucsi->cci, le32_to_cpu(*(__le32 *)(ua->base + UCSI_CCI)));
+
+ return 0;
+}
+
+static int ucsi_acpi_read(struct ucsi *ucsi, unsigned int offset,
+ void *val, size_t val_len)
+{
+ struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
+
memcpy(val, ua->base + offset, val_len);
return 0;
@@ -66,7 +74,6 @@ static int ucsi_acpi_async_write(struct ucsi *ucsi, unsigned int offset,
struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
memcpy(ua->base + offset, val, val_len);
- ua->cmd = *(u64 *)val;
return ucsi_acpi_dsm(ua, UCSI_DSM_FUNC_WRITE);
}
@@ -100,34 +107,12 @@ static int ucsi_acpi_sync_write(struct ucsi *ucsi, unsigned int offset,
}
static const struct ucsi_operations ucsi_acpi_ops = {
+ .poll_cci = ucsi_acpi_poll_cci,
.read = ucsi_acpi_read,
.sync_write = ucsi_acpi_sync_write,
.async_write = ucsi_acpi_async_write
};
-static int
-ucsi_zenbook_read(struct ucsi *ucsi, unsigned int offset, void *val, size_t val_len)
-{
- struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
- int ret;
-
- if (offset == UCSI_VERSION || UCSI_COMMAND(ua->cmd) == UCSI_PPM_RESET) {
- ret = ucsi_acpi_dsm(ua, UCSI_DSM_FUNC_READ);
- if (ret)
- return ret;
- }
-
- memcpy(val, ua->base + offset, val_len);
-
- return 0;
-}
-
-static const struct ucsi_operations ucsi_zenbook_ops = {
- .read = ucsi_zenbook_read,
- .sync_write = ucsi_acpi_sync_write,
- .async_write = ucsi_acpi_async_write
-};
-
/*
* Some Dell laptops expect that an ACK command with the
* UCSI_ACK_CONNECTOR_CHANGE bit set is followed by a (separate)
@@ -177,19 +162,13 @@ ucsi_dell_sync_write(struct ucsi *ucsi, unsigned int offset,
}
static const struct ucsi_operations ucsi_dell_ops = {
+ .poll_cci = ucsi_acpi_poll_cci,
.read = ucsi_acpi_read,
.sync_write = ucsi_dell_sync_write,
.async_write = ucsi_acpi_async_write
};
static const struct dmi_system_id ucsi_acpi_quirks[] = {
- {
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
- DMI_MATCH(DMI_PRODUCT_NAME, "ZenBook UX325UA_UM325UA"),
- },
- .driver_data = (void *)&ucsi_zenbook_ops,
- },
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
@@ -203,11 +182,9 @@ static void ucsi_acpi_notify(acpi_handle handle, u32 event, void *data)
{
struct ucsi_acpi *ua = data;
u32 cci;
- int ret;
- ret = ua->ucsi->ops->read(ua->ucsi, UCSI_CCI, &cci, sizeof(cci));
- if (ret)
- return;
+ cci = le32_to_cpu(*(__le32 *)(ua->base + UCSI_CCI));
+ WRITE_ONCE(ua->ucsi->cci, cci);
if (UCSI_CCI_CONNECTOR(cci))
ucsi_connector_change(ua->ucsi, UCSI_CCI_CONNECTOR(cci));
@@ -194,7 +194,6 @@ struct ucsi_ccg_altmode {
#define CCGX_MESSAGE_IN_MAX 16
struct op_region {
- __le32 cci;
u8 message_in[CCGX_MESSAGE_IN_MAX];
};
@@ -338,7 +337,6 @@ static int ccg_op_region_update(struct ucsi_ccg *uc, u32 cci)
}
spin_lock(&uc->op_lock);
- data->cci = cpu_to_le32(cci);
if (UCSI_CCI_LENGTH(cci))
memcpy(&data->message_in, buf, size);
spin_unlock(&uc->op_lock);
@@ -556,32 +554,24 @@ static void ucsi_ccg_nvidia_altmode(struct ucsi_ccg *uc,
}
}
+static int ucsi_ccg_poll_cci(struct ucsi *ucsi)
+{
+ return 0;
+}
+
static int ucsi_ccg_read(struct ucsi *ucsi, unsigned int offset,
void *val, size_t val_len)
{
struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi);
- u16 reg = CCGX_RAB_UCSI_DATA_BLOCK(offset);
struct ucsi_capability *cap;
struct ucsi_altmode *alt;
- int ret = 0;
-
- if (offset == UCSI_CCI) {
- spin_lock(&uc->op_lock);
- memcpy(val, &(uc->op_data).cci, val_len);
- spin_unlock(&uc->op_lock);
- } else if (offset == UCSI_MESSAGE_IN) {
- spin_lock(&uc->op_lock);
- memcpy(val, &(uc->op_data).message_in, val_len);
- spin_unlock(&uc->op_lock);
- } else {
- ret = ccg_read(uc, reg, val, val_len);
- }
-
- if (ret)
- return ret;
if (offset != UCSI_MESSAGE_IN)
- return ret;
+ return -EINVAL;
+
+ spin_lock(&uc->op_lock);
+ memcpy(val, &uc->op_data.message_in, val_len);
+ spin_unlock(&uc->op_lock);
switch (UCSI_COMMAND(uc->last_cmd_sent)) {
case UCSI_GET_CURRENT_CAM:
@@ -607,7 +597,7 @@ static int ucsi_ccg_read(struct ucsi *ucsi, unsigned int offset,
}
uc->last_cmd_sent = 0;
- return ret;
+ return 0;
}
static int ucsi_ccg_async_write(struct ucsi *ucsi, unsigned int offset,
@@ -620,9 +610,7 @@ static int ucsi_ccg_async_write(struct ucsi *ucsi, unsigned int offset,
* UCSI may read CCI instantly after async_write,
* clear CCI to avoid caller getting wrong data before we get CCI from ISR
*/
- spin_lock(&uc->op_lock);
- uc->op_data.cci = 0;
- spin_unlock(&uc->op_lock);
+ WRITE_ONCE(ucsi->cci, 0);
return ccg_write(uc, reg, val, val_len);
}
@@ -667,6 +655,7 @@ static int ucsi_ccg_sync_write(struct ucsi *ucsi, unsigned int offset,
}
static const struct ucsi_operations ucsi_ccg_ops = {
+ .poll_cci = ucsi_ccg_poll_cci,
.read = ucsi_ccg_read,
.sync_write = ucsi_ccg_sync_write,
.async_write = ucsi_ccg_async_write,
@@ -695,6 +684,7 @@ static irqreturn_t ccg_irq_handler(int irq, void *data)
if (ret)
goto err_clear_irq;
cci = le32_to_cpu(__cci);
+ WRITE_ONCE(uc->ucsi->cci, cci);
if (UCSI_CCI_CONNECTOR(cci))
ucsi_connector_change(uc->ucsi, UCSI_CCI_CONNECTOR(cci));
@@ -77,10 +77,8 @@ struct pmic_glink_ucsi {
u8 read_buf[UCSI_BUF_SIZE];
};
-static int pmic_glink_ucsi_read(struct ucsi *__ucsi, unsigned int offset,
- void *val, size_t val_len)
+static int pmic_glink_sync_read_buf(struct pmic_glink_ucsi *ucsi)
{
- struct pmic_glink_ucsi *ucsi = ucsi_get_drvdata(__ucsi);
struct ucsi_read_buf_req_msg req = {};
unsigned long left;
int ret;
@@ -106,7 +104,6 @@ static int pmic_glink_ucsi_read(struct ucsi *__ucsi, unsigned int offset,
goto out_unlock;
}
- memcpy(val, &ucsi->read_buf[offset], val_len);
ret = 0;
out_unlock:
@@ -115,6 +112,35 @@ static int pmic_glink_ucsi_read(struct ucsi *__ucsi, unsigned int offset,
return ret;
}
+static int pmic_glink_ucsi_poll_cci(struct ucsi *__ucsi)
+{
+ struct pmic_glink_ucsi *ucsi = ucsi_get_drvdata(__ucsi);
+ int ret;
+
+ ret = pmic_glink_sync_read_buf(ucsi);
+ if (ret)
+ return ret;
+
+ mutex_lock(&ucsi->lock);
+ WRITE_ONCE(__ucsi->cci,
+ le32_to_cpu(*(__le32 *)&ucsi->read_buf[UCSI_CCI]));
+ mutex_unlock(&ucsi->lock);
+
+ return 0;
+}
+
+static int pmic_glink_ucsi_read(struct ucsi *__ucsi, unsigned int offset,
+ void *val, size_t val_len)
+{
+ struct pmic_glink_ucsi *ucsi = ucsi_get_drvdata(__ucsi);
+
+ mutex_lock(&ucsi->lock);
+ memcpy(val, &ucsi->read_buf[offset], val_len);
+ mutex_unlock(&ucsi->lock);
+
+ return 0;
+}
+
static int pmic_glink_ucsi_locked_write(struct pmic_glink_ucsi *ucsi, unsigned int offset,
const void *val, size_t val_len)
{
@@ -187,6 +213,7 @@ static int pmic_glink_ucsi_sync_write(struct ucsi *__ucsi, unsigned int offset,
}
static const struct ucsi_operations pmic_glink_ucsi_ops = {
+ .poll_cci = pmic_glink_ucsi_poll_cci,
.read = pmic_glink_ucsi_read,
.sync_write = pmic_glink_ucsi_sync_write,
.async_write = pmic_glink_ucsi_async_write
@@ -221,11 +248,15 @@ static void pmic_glink_ucsi_notify(struct work_struct *work)
u32 cci;
int ret;
- ret = pmic_glink_ucsi_read(ucsi->ucsi, UCSI_CCI, &cci, sizeof(cci));
+ ret = pmic_glink_sync_read_buf(ucsi);
if (ret) {
- dev_err(ucsi->dev, "failed to read CCI on notification\n");
+ dev_err(ucsi->dev, "failed to sync read buf\n");
return;
}
+ mutex_lock(&ucsi->lock);
+ cci = le32_to_cpu(*(__le32 *)&ucsi->read_buf[UCSI_CCI]);
+ mutex_unlock(&ucsi->lock);
+ WRITE_ONCE(ucsi->ucsi->cci, cci);
con_num = UCSI_CCI_CONNECTOR(cci);
if (con_num) {
@@ -258,6 +289,11 @@ static void pmic_glink_ucsi_register(struct work_struct *work)
__le16 version;
int ret;
+ ret = pmic_glink_sync_read_buf(ucsi);
+ if (ret < 0) {
+ dev_err(ucsi->dev, "cannot sync read buf: %d\n", ret);
+ return;
+ }
ret = pmic_glink_ucsi_read(ucsi->ucsi, UCSI_VERSION, &version,
sizeof(version));
if (ret < 0) {
@@ -358,6 +358,20 @@ static int ucsi_stm32g0_read_from_hw(struct ucsi_stm32g0 *g0,
}
/* UCSI ops */
+static int ucsi_stm32g0_poll_cci(struct ucsi *ucsi)
+{
+ struct ucsi_stm32g0 *g0 = ucsi_get_drvdata(ucsi);
+ __le32 cci;
+ int ret;
+
+ ret = ucsi_stm32g0_read_from_hw(g0, UCSI_CCI, &cci, sizeof(cci));
+ if (ret)
+ return ret;
+ WRITE_ONCE(ucsi->cci, le32_to_cpu(cci));
+
+ return 0;
+}
+
static int ucsi_stm32g0_read(struct ucsi *ucsi, unsigned int offset,
void *val, size_t len)
{
@@ -424,15 +438,18 @@ static int ucsi_stm32g0_sync_write(struct ucsi *ucsi, unsigned int offset, const
static irqreturn_t ucsi_stm32g0_irq_handler(int irq, void *data)
{
struct ucsi_stm32g0 *g0 = data;
+ __le32 __cci;
u32 cci;
int ret;
if (g0->suspended)
g0->wakeup_event = true;
- ret = ucsi_stm32g0_read(g0->ucsi, UCSI_CCI, &cci, sizeof(cci));
+ ret = ucsi_stm32g0_read_from_hw(g0, UCSI_CCI, &__cci, sizeof(__cci));
if (ret)
return IRQ_NONE;
+ cci = le32_to_cpu(__cci);
+ WRITE_ONCE(g0->ucsi->cci, cci);
if (UCSI_CCI_CONNECTOR(cci))
ucsi_connector_change(g0->ucsi, UCSI_CCI_CONNECTOR(cci));
@@ -445,6 +462,7 @@ static irqreturn_t ucsi_stm32g0_irq_handler(int irq, void *data)
}
static const struct ucsi_operations ucsi_stm32g0_ops = {
+ .poll_cci = ucsi_stm32g0_poll_cci,
.read = ucsi_stm32g0_read,
.sync_write = ucsi_stm32g0_sync_write,
.async_write = ucsi_stm32g0_async_write,
@@ -626,7 +644,7 @@ static int ucsi_stm32g0_probe_bootloader(struct ucsi *ucsi)
* Try to guess if the STM32G0 is running a UCSI firmware. First probe the UCSI FW at its
* i2c address. Fallback to bootloader i2c address only if firmware-name is specified.
*/
- ret = ucsi_stm32g0_read(ucsi, UCSI_VERSION, &ucsi_version, sizeof(ucsi_version));
+ ret = ucsi_stm32g0_read_from_hw(g0, UCSI_VERSION, &ucsi_version, sizeof(ucsi_version));
if (!ret || !g0->fw_name)
return ret;