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_entry
	struct work_struct		notify_work;//ucsi_qti_notify_work
	struct work_struct		setup_work;//ucsi_qti_setup_work
	struct 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->client
	if (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);//create
	if (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 ucsi
	if (!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_ops

	return 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_read
	if (ret)
		return ret;

	if (!ucsi->version)
		return -ENODEV;

	queue_delayed_work(system_long_wq, &ucsi->work, 0);//ucsi_init_work

	return 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);//init
	if (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 data
	if (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;
}

 

 

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值