/** * ucsi_unregister_ppm - Unregister UCSI PPM Interface * @ucsi: struct ucsi associated with the PPM * * Unregister UCSI PPM that was created with ucsi_register(). */ void ucsi_unregister_ppm(struct ucsi *ucsi) { struct ucsi_control ctrl; int i; /* Make sure that we are not in the middle of driver initialization */ cancel_work_sync(&ucsi->work); mutex_lock(&ucsi->ppm_lock); /* Disable everything except command complete notification */ UCSI_CMD_SET_NTFY_ENABLE(ctrl, UCSI_ENABLE_NTFY_CMD_COMPLETE) ucsi_run_command(ucsi, &ctrl, NULL, 0); mutex_unlock(&ucsi->ppm_lock); for (i = 0; i < ucsi->cap.num_connectors; i++) { cancel_work_sync(&ucsi->connector[i].work); ucsi_unregister_partner(&ucsi->connector[i]); typec_unregister_port(ucsi->connector[i].port); } ucsi_reset_ppm(ucsi); kfree(ucsi->connector); kfree(ucsi); }
static int ucsi_acpi_remove(struct platform_device *pdev) { struct ucsi *ucsi = platform_get_drvdata(pdev); acpi_remove_notify_handler(ACPI_HANDLE(&pdev->dev), ACPI_ALL_NOTIFY, ucsi_acpi_notify); /* Make sure there are no events in the middle of being processed */ if (wait_on_bit_timeout(&ucsi->flags, EVENT_PENDING, TASK_UNINTERRUPTIBLE, msecs_to_jiffies(UCSI_TIMEOUT_MS))) dev_WARN(ucsi->dev, "%s: Events still pending\n", __func__); ucsi_reset_ppm(ucsi); return 0; }
static int ucsi_role_cmd(struct ucsi_connector *con, struct ucsi_control *ctrl) { int ret; ret = ucsi_run_command(con->ucsi, ctrl, NULL, 0); if (ret == -ETIMEDOUT) { struct ucsi_control c; /* PPM most likely stopped responding. Resetting everything. */ ucsi_reset_ppm(con->ucsi); UCSI_CMD_SET_NTFY_ENABLE(c, UCSI_ENABLE_NTFY_ALL); ucsi_run_command(con->ucsi, &c, NULL, 0); ucsi_reset_connector(con, true); } return ret; }
static int ucsi_init(struct ucsi *ucsi) { struct ucsi_connector *con; struct ucsi_control ctrl; int ret; int i; init_completion(&ucsi->complete); spin_lock_init(&ucsi->dev_lock); mutex_init(&ucsi->ppm_lock); /* Reset the PPM */ ret = ucsi_reset_ppm(ucsi); if (ret) return ret; /* * REVISIT: Executing second reset to WA an issue seen on some of the * Broxton based platforms, where the first reset puts the PPM into a * state where it's unable to recognise some of the commands. */ ret = ucsi_reset_ppm(ucsi); if (ret) return ret; mutex_lock(&ucsi->ppm_lock); /* Enable basic notifications */ ctrl.cmd.cmd = UCSI_SET_NOTIFICATION_ENABLE; ctrl.cmd.length = 0; ctrl.cmd.data = UCSI_ENABLE_NTFY_CMD_COMPLETE | UCSI_ENABLE_NTFY_ERROR; ret = ucsi_run_cmd(ucsi, &ctrl, NULL, 0); if (ret) goto err_reset; /* Get PPM capabilities */ ctrl.cmd.cmd = UCSI_GET_CAPABILITY; ret = ucsi_run_cmd(ucsi, &ctrl, &ucsi->cap, sizeof(ucsi->cap)); if (ret) goto err_reset; if (!ucsi->cap.num_connectors) { ret = -ENODEV; goto err_reset; } ucsi->connector = devm_kcalloc(ucsi->dev, ucsi->cap.num_connectors, sizeof(*ucsi->connector), GFP_KERNEL); if (!ucsi->connector) { ret = -ENOMEM; goto err_reset; } for (i = 1, con = ucsi->connector; i < ucsi->cap.num_connectors + 1; i++, con++) { /* Get connector capability */ ctrl.cmd.cmd = UCSI_GET_CONNECTOR_CAPABILITY; ctrl.cmd.data = i; ret = ucsi_run_cmd(ucsi, &ctrl, &con->cap, sizeof(con->cap)); if (ret) goto err_reset; con->num = i; con->ucsi = ucsi; INIT_WORK(&con->work, ucsi_connector_change); } /* Enable all notifications */ ctrl.cmd.cmd = UCSI_SET_NOTIFICATION_ENABLE; ctrl.cmd.data = UCSI_ENABLE_NTFY_ALL; ret = ucsi_run_cmd(ucsi, &ctrl, NULL, 0); if (ret < 0) goto err_reset; mutex_unlock(&ucsi->ppm_lock); return 0; err_reset: ucsi_reset_ppm(ucsi); mutex_unlock(&ucsi->ppm_lock); return ret; }
static void ucsi_init(struct work_struct *work) { struct ucsi *ucsi = container_of(work, struct ucsi, work); struct ucsi_connector *con; struct ucsi_control ctrl; int ret; int i; mutex_lock(&ucsi->ppm_lock); /* Reset the PPM */ ret = ucsi_reset_ppm(ucsi); if (ret) { dev_err(ucsi->dev, "failed to reset PPM!\n"); goto err; } /* 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); 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)); if (ret < 0) goto err_reset; if (!ucsi->cap.num_connectors) { ret = -ENODEV; goto err_reset; } /* Allocate the connectors. Released in ucsi_unregister_ppm() */ ucsi->connector = kcalloc(ucsi->cap.num_connectors + 1, sizeof(*ucsi->connector), GFP_KERNEL); if (!ucsi->connector) { ret = -ENOMEM; goto err_reset; } /* Register all connectors */ for (i = 0; i < ucsi->cap.num_connectors; i++) { ret = ucsi_register_port(ucsi, i); if (ret) goto err_unregister; } /* Enable all notifications */ UCSI_CMD_SET_NTFY_ENABLE(ctrl, UCSI_ENABLE_NTFY_ALL); ret = ucsi_run_command(ucsi, &ctrl, NULL, 0); if (ret < 0) goto err_unregister; mutex_unlock(&ucsi->ppm_lock); return; err_unregister: for (con = ucsi->connector; con->port; con++) { ucsi_unregister_partner(con); typec_unregister_port(con->port); con->port = NULL; } err_reset: ucsi_reset_ppm(ucsi); err: mutex_unlock(&ucsi->ppm_lock); dev_err(ucsi->dev, "PPM init failed (%d)\n", ret); }