qcom ucsi probe
ucsi glink 注册一个ucsi 设备,和pmic glink进行通信,ucsi作为pmic glink的一个client。
lkml的patch https://lkml.org/lkml/2023/1/30/233
dtsi中一般会定义 qcom,ucsi-glink 信息,用于和驱动进行匹配
static const struct of_device_id ucsi_match_table[] = {
{.compatible = "qcom,ucsi-glink"},
{},
};static struct platform_driver ucsi_driver = {
.driver = {
.name = "ucsi_glink",
.of_match_table = ucsi_match_table,
},
.probe = ucsi_probe,
.remove = ucsi_remove,
};module_platform_driver(ucsi_driver);
MODULE_DESCRIPTION("QTI UCSI Glink driver");
MODULE_LICENSE("GPL v2");
ucsi probe都进行了哪些工作呢
ucsi_dev 结构解析
struct ucsi_dev {struct device *dev;struct ucsi *ucsi;//用于与 UCSI 核心逻辑进行交互。负责处理相关的连接和协议逻辑struct pmic_glink_client *client;//指向 PMIC GLink 客户端结构体的指针,用于与 PMIC GLink 通信接口进行交互,实现电源管理和通信struct completion read_ack;struct completion write_ack;struct completion sync_write_ack;struct mutex read_lock;struct mutex write_lock;struct mutex notify_lock;struct mutex state_lock;struct ucsi_read_buf_resp_msg rx_buf;//存储从设备接收到的数据缓冲区响应消息unsigned long flags;atomic_t rx_valid;unsigned long cmd_requested_flags;struct list_head constat_info_list;//head ,node is constat_info_entrystruct work_struct notify_work;//ucsi_qti_notify_workstruct work_struct setup_work;//ucsi_qti_setup_workstruct work_struct unregister_work;atomic_t state;
};
static int ucsi_probe(struct platform_device *pdev)
{struct device *dev = &pdev->dev;struct pmic_glink_client_data client_data;struct ucsi_dev *udev;int rc;udev = devm_kzalloc(dev, sizeof(*udev), GFP_KERNEL);//创建并进行初始化if (!udev)return -ENOMEM;INIT_LIST_HEAD(&udev->constat_info_list);INIT_WORK(&udev->notify_work, ucsi_qti_notify_work);INIT_WORK(&udev->setup_work, ucsi_qti_setup_work);INIT_WORK(&udev->unregister_work, ucsi_qti_unregister_work);//注销?销毁mutex_init(&udev->read_lock);mutex_init(&udev->write_lock);mutex_init(&udev->notify_lock);mutex_init(&udev->state_lock);init_completion(&udev->read_ack);init_completion(&udev->write_ack);init_completion(&udev->sync_write_ack);atomic_set(&udev->rx_valid, 0);atomic_set(&udev->state, PMIC_GLINK_STATE_UP);client_data.id = MSG_OWNER_UC;client_data.name = "ucsi";client_data.msg_cb = ucsi_callback;client_data.priv = udev;client_data.state_cb = ucsi_qti_state_cb;udev->client = pmic_glink_register_client(dev, &client_data);//client data->clientif (IS_ERR(udev->client)) {rc = PTR_ERR(udev->client);if (rc != -EPROBE_DEFER)dev_err(dev, "Error in registering with pmic_glink rc=%d\n",rc);return rc;}platform_set_drvdata(pdev, udev);udev->dev = dev;ucsi_ipc_log = ipc_log_context_create(NUM_LOG_PAGES, "ucsi", 0);if (!ucsi_ipc_log)dev_warn(dev, "Error in creating ipc_log_context\n");rc = ucsi_setup(udev);if (rc) {ipc_log_context_destroy(ucsi_ipc_log);ucsi_ipc_log = NULL;pmic_glink_unregister_client(udev->client);}return rc;
}
ucsi_setup的过程如下
static int ucsi_setup(struct ucsi_dev *udev)
{int rc;if (udev->ucsi) {dev_err(udev->dev, "ucsi is not NULL\n");return -EINVAL;//已经有了,不需要创建了}mutex_lock(&udev->state_lock);udev->ucsi = ucsi_create(udev->dev, &ucsi_qti_ops);//createif (IS_ERR(udev->ucsi)) {rc = PTR_ERR(udev->ucsi);dev_err(udev->dev, "ucsi_create failed rc=%d\n", rc);udev->ucsi = NULL;mutex_unlock(&udev->state_lock);return rc;}ucsi_set_drvdata(udev->ucsi, udev);rc = ucsi_register(udev->ucsi);//then 注册if (rc) {dev_err(udev->dev, "ucsi_register failed rc=%d\n", rc);ucsi_destroy(udev->ucsi);udev->ucsi = NULL;mutex_unlock(&udev->state_lock);return rc;}mutex_unlock(&udev->state_lock);return 0;
}
其中的static const struct ucsi_operations ucsi_qti_ops = {
.read = ucsi_qti_read,
.sync_write = ucsi_qti_sync_write,
.async_write = ucsi_qti_async_write
};
/*** 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);//new ucsiif (!ucsi)return ERR_PTR(-ENOMEM);INIT_WORK(&ucsi->resume_work, ucsi_resume_work);INIT_DELAYED_WORK(&ucsi->work, ucsi_init_work);mutex_init(&ucsi->ppm_lock);ucsi->dev = dev;ucsi->ops = ops;//ucsi_qti_opsreturn ucsi;
}
*** 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, //read ucsi版本号sizeof(ucsi->version));//ucsi_qti_readif (ret)return ret;if (!ucsi->version)return -ENODEV;queue_delayed_work(system_long_wq, &ucsi->work, 0);//ucsi_init_workreturn 0;
}
static void ucsi_init_work(struct work_struct *work)
{struct ucsi *ucsi = container_of(work, struct ucsi, work.work);int ret;ret = ucsi_init(ucsi);//initif (ret)dev_err(ucsi->dev, "PPM init failed (%d)\n", ret);if (ret == -EPROBE_DEFER) {if (ucsi->work_count++ > UCSI_ROLE_SWITCH_WAIT_COUNT)return;queue_delayed_work(system_long_wq, &ucsi->work,UCSI_ROLE_SWITCH_INTERVAL);}
}
/*** ucsi_init - Initialize UCSI interface* @ucsi: UCSI to be initialized** Registers all ports @ucsi has and enables all notification events.*/
static int ucsi_init(struct ucsi *ucsi)
{struct ucsi_connector *con, *connector;u64 command, ntfy;int ret;int i;/* Reset the PPM */ret = ucsi_reset_ppm(ucsi);if (ret) {dev_err(ucsi->dev, "failed to reset PPM!\n");goto err;}/* Enable basic notifications */ntfy = UCSI_ENABLE_NTFY_CMD_COMPLETE | UCSI_ENABLE_NTFY_ERROR;command = UCSI_SET_NOTIFICATION_ENABLE | ntfy;ret = ucsi_send_command(ucsi, command, NULL, 0);if (ret < 0)goto err_reset;/* Get PPM capabilities */command = UCSI_GET_CAPABILITY;ret = ucsi_send_command(ucsi, command, &ucsi->cap, sizeof(ucsi->cap));//get cap dataif (ret < 0)goto err_reset;if (!ucsi->cap.num_connectors) {ret = -ENODEV;goto err_reset;}/* Allocate the connectors. Released in ucsi_unregister() */connector = kcalloc(ucsi->cap.num_connectors + 1, sizeof(*connector), GFP_KERNEL);if (!connector) {ret = -ENOMEM;goto err_reset;}//创建connector/* Register all connectors */for (i = 0; i < ucsi->cap.num_connectors; i++) {connector[i].num = i + 1;ret = ucsi_register_port(ucsi, &connector[i]);if (ret)goto err_unregister;}/* Enable all notifications */ntfy = UCSI_ENABLE_NTFY_ALL;command = UCSI_SET_NOTIFICATION_ENABLE | ntfy;ret = ucsi_send_command(ucsi, command, NULL, 0);if (ret < 0)goto err_unregister;ucsi->connector = connector;//connetor数组ucsi->ntfy = ntfy;return 0;err_unregister:for (con = connector; con->port; con++) {ucsi_unregister_partner(con);ucsi_unregister_altmodes(con, UCSI_RECIPIENT_CON);ucsi_unregister_port_psy(con);if (con->wq)destroy_workqueue(con->wq);typec_unregister_port(con->port);con->port = NULL;}kfree(connector);
err_reset:memset(&ucsi->cap, 0, sizeof(ucsi->cap));ucsi_reset_ppm(ucsi);
err:return ret;
}
ucsi_send_command是一个发送命令返回数据的函数,遵循ucsi协议发送命令后读取状态和数据。ppm执行命令后,opm发送ack确认命令收到并正确进行了处理
- 功能:
ucsi_send_command
函数主要负责发送一个 UCSI 命令,并在需要时从设备读取响应数据,然后确认命令完成。 - 锁机制:使用互斥锁
ppm_lock
确保对 UCSI 设备的操作是线程安全的。 - 命令执行:首先执行命令,并获取命令的长度或相关信息。
- 数据读取:如果需要,从设备读取数据。
- 确认命令:执行完命令后确认命令完成。
- 错误处理:函数通过
goto out
跳转到错误处理标签,确保在发生错误时能够适当释放锁。 - 返回值:返回命令的长度或其他相关信息,或在出现错误时返回错误代码。
int ucsi_send_command(struct ucsi *ucsi, u64 command,void *data, size_t size)
{u8 length;int ret;mutex_lock(&ucsi->ppm_lock);//互斥锁ret = ucsi_exec_command(ucsi, command);if (ret < 0)goto out;length = ret;if (data) {ret = ucsi->ops->read(ucsi, UCSI_MESSAGE_IN, data, size);if (ret)goto out;}ret = ucsi_acknowledge_command(ucsi);if (ret)goto out;ret = length;
out:mutex_unlock(&ucsi->ppm_lock);return ret;
}