3.MTK充电之PD芯片驱动总结
·
概述
****在mtk6769-A15-kernel-6.6平台,介绍upm7610 pd芯片驱动,添加pd芯片驱动(与ap使用i2c通信)主要是解决使用pd快充方案的需求,与pd充电器实现pd协议通信,达到升压提升充电功率目的。
1.upm7610_i2c_probe函数
~~分析驱动我们从upm7610_i2c_probe开始,一般就是先I2C 适配器(I2C Adapter)是否支持特定的通信功能,获取chip_id,解析设备树,寄存器参数初始化、Type-C 端口控制器初始化、中断告警初始化。
static int upm7610_i2c_probe(struct i2c_client *client)
{
struct upm7610_chip *chip;
int ret = 0, chip_id;
bool use_dt = client->dev.of_node;
pr_info("%s (%s)\n", __func__, UPM7610_DRV_VERSION);
/* 检查i2c适配器是否支持块传输及单字节读取 */
if (i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_I2C_BLOCK | I2C_FUNC_SMBUS_BYTE_DATA))
pr_info("I2C functionality : OK...\n");
else
pr_info("I2C functionality check : failuare...\n");
/* 获取chip_id */
chip_id = upm7610_check_revision(client);
if (chip_id < 0)
return chip_id;
chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
return -ENOMEM;
if (use_dt) {
/*解析设备树*/
ret = up_parse_dt(chip, &client->dev);
if (ret < 0)
return ret;
} else {
dev_err(&client->dev, "no dts node\n");
return -ENODEV;
}
chip->dev = &client->dev;
chip->client = client;
i2c_set_clientdata(client, chip);
chip->chip_id = chip_id;
pr_info("upm7610_chipID = 0x%0x\n", chip_id);
mutex_init(&chip->irq_lock);
chip->is_suspended = false;
chip->irq_while_suspended = false;
chip->vconn_en = false;
chip->lpm_en = false;
/* 寄存器初始化 */
ret = upm7610_regmap_init(chip);
if (ret < 0) {
dev_err(chip->dev, "upm7610 regmap init fail\n");
goto err_regmap_init;
}
/* Type-c port control初始化 */
ret = upm7610_tcpcdev_init(chip, &client->dev);
if (ret < 0) {
dev_err(&client->dev, "upm7610 tcpc dev init fail\n");
goto err_tcpc_reg;
}
/* 中断告警初始化 */
ret = upm7610_init_alert(chip->tcpc);
if (ret < 0) {
pr_err("upm7610 init alert fail\n");
goto err_irq_init;
}
pr_info("%s probe OK!\n", __func__);
return 0;
err_irq_init:
tcpc_device_unregister(chip->dev, chip->tcpc);
err_tcpc_reg:
upm7610_regmap_deinit(chip);
err_regmap_init:
mutex_destroy(&chip->irq_lock);
return ret;
}
这里注重看设备树配置、Type-c port control初始化、中断告警初始化,其他都没啥东西。
2.dts配置
&i2c4 {
upm7610_typec: upm7610@60 {
compatible = "unisemipower,upm7610"; /* 驱动匹配标签 */
reg = <0x60>; /* 芯片i2c地址 */
status = "okay"; /* 设备树配置使能 */
up-tcpc,name = "type_c_port0"; /* type-c端口控制器名字 */
up-tcpc,role-def = <5>; /* 0: Unknown, 1: SNK, 2: SRC 配置控制器角色*/
/* 3: DRP, 4: Try.SRC, 5: Try.SNK */
up-tcpc,rp-level = <0>; /* 0: Default, 1: 1.5, 2: 3.0 供电能力*/
up-tcpc,vconn-supply = <1>; /* 0: Never, 1: Always, */
/* 2: EMarkOnly, 3: StartOnly */
upm7610pd,intr-gpio = <&pio 41 0x0>; /* gpio中断配置 */
upm7610pd,intr-gpio-num = <41>;
/* charger = <&mt6375_chg>; */
pd-data {
pd,vid = <0x362f>;
pd,pid = <0x7610>;
/* 能够提供的供电能力 */
pd,source-cap-ext = /bits/ 8 <0xcf 0x29 0x11 0x17
0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00
0x00 0x00 0x01 0x02
0x00>;
/* 能够接受的受电能力 */
pd,sink-cap-ext = /bits/ 8 <0xcf 0x29 0x11 0x17
0x00 0x00 0x00 0x00
0x00 0x00 0x01 0x00
0x00 0x00 0x00 0x00
0x01 0x0b 0x01 0x0a
0x0a 0x00 0x00 0x00>;
pd,mfrs = "UnisemipowerTCPC";
/*
* VSAFE5V = 0, MAX_POWER = 1, CUSTOM = 2,
* MAX_POWER_LV = 0x21, MAX_POWER_LVIC = 0x31
* MAX_POWER_HV = 0x41, MAX_POWER_HVIC = 0x51
*/
pd,charging-policy = <0x31>;
pd,source-pdo-size = <1>;
pd,source-pdo-data = <0x00019032>; /*端口数据输出*/
pd,sink-pdo-size = <1>;
pd,sink-pdo-data = <0x000190c8>;
pd,id-vdo-size = <6>;
pd,id-vdo-data = <0xd140362f 0x0 0x76100000
0x61000000 0x0 0x41000000>;
bat,nr = <1>;
pd,country-nr = <0>;
bat-info0 {
bat,vid = <0x362f>;
bat,pid = <0x7610>;
bat,mfrs = "bat1";
bat,design-cap = <3000>;
};
};
dpm-caps {
local-dr-power;
local-dr-data;
// local-ext-power;
local-usb-comm;
// local-usb-suspend;
// local-high-cap;
// local-give-back;
local-no-suspend;
/* With DP Source capability */
// attempt-enter-dp-mode;
// attempt-discover-cable;
// attempt-discover-id;
// attempt-discover-svids;
/* 0: disable, 1: prefer-snk, 2: prefer-src */
pr-check = <0>;
// pr-reject-as-source;
// pr-reject-as-sink;
// pr-check-gp-source;
// pr-check-gp-sink;
/* 0: disable, 1: prefer-ufp, 2: prefer-dfp */
dr-check = <0>;
// dr-reject-as-dfp;
// dr-reject-as-ufp;
};
displayport {
/* connection type = "both", "ufp-d", "dfp-d" */
1st-connection = "dfp-d";
2nd-connection = "dfp-d";
// usbr20-not-used;
typec,receptacle;
ufp-d {
// pin-assignment,mode-c;
// pin-assignment,mode-d;
// pin-assignment,mode-e;
};
dfp-d {
pin-assignment,mode-c;
pin-assignment,mode-d;
pin-assignment,mode-e;
};
};
};
};
3. 解析 upm7610_tcpcdev_init 函数
****这里边比较重要的函数就是tcpc_device_register跟tcpci核心层建立联系,这个函数实现在tcpci_core.c中,将Type-C端口控制器物理层的操作方法集注册到核心层。
static int upm7610_tcpcdev_init(struct upm7610_chip *chip, struct device *dev)
{
struct tcpc_desc *desc;
struct tcpc_device *tcpc = NULL;
struct device_node *np = dev->of_node;
u32 val, len;
const char *name = "default";
dev_info(dev, "%s\n", __func__);
desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
if (!desc)
return -ENOMEM;
/*type-c角色*/
if (of_property_read_u32(np, "up-tcpc,role-def", &val) >= 0 ||
of_property_read_u32(np, "up-tcpc,role_def", &val) >= 0) {
if (val >= TYPEC_ROLE_NR)
desc->role_def = TYPEC_ROLE_DRP;
else
desc->role_def = val;
} else {
dev_info(dev, "use default Role DRP\n");
desc->role_def = TYPEC_ROLE_DRP;
}
if (of_property_read_u32(np, "up-tcpc,rp-level", &val) >= 0 ||
of_property_read_u32(np, "up-tcpc,rp_level", &val) >= 0) {
switch (val) {
case TYPEC_RP_DFT:
case TYPEC_RP_1_5:
case TYPEC_RP_3_0:
desc->rp_lvl = val;
break;
default:
desc->rp_lvl = TYPEC_RP_DFT;
break;
}
}
if (of_property_read_u32(np, "up-tcpc,vconn-supply", &val) >= 0 ||
of_property_read_u32(np, "up-tcpc,vconn_supply", &val) >= 0) {
if (val >= TCPC_VCONN_SUPPLY_NR)
desc->vconn_supply = TCPC_VCONN_SUPPLY_ALWAYS;
else
desc->vconn_supply = val;
} else {
dev_info(dev, "use default VconnSupply\n");
desc->vconn_supply = TCPC_VCONN_SUPPLY_ALWAYS;
}
if (of_property_read_string(np, "up-tcpc,name",
(char const **)&name) < 0) {
dev_info(dev, "use default name\n");
}
len = strlen(name);
desc->name = kzalloc(len+1, GFP_KERNEL);
if (!desc->name)
return -ENOMEM;
strlcpy((char *)desc->name, name, len+1);
chip->tcpc_desc = desc;
/*非常重要*/
tcpc = tcpc_device_register(dev, desc, &upm7610_tcpc_ops, chip);
if (IS_ERR_OR_NULL(tcpc))
return -EINVAL;
chip->tcpc = tcpc;
#if CONFIG_USB_PD_DISABLE_PE
tcpc->disable_pe = of_property_read_bool(np, "up-tcpc,disable-pe") ||
of_property_read_bool(np, "up-tcpc,disable_pe");
#endif /* CONFIG_USB_PD_DISABLE_PE */
tcpc->tcpc_flags = TCPC_FLAGS_VCONN_SAFE5V_ONLY;
#if CONFIG_USB_PD_RETRY_CRC_DISCARD
tcpc->tcpc_flags |= TCPC_FLAGS_RETRY_CRC_DISCARD;
#endif /* CONFIG_USB_PD_RETRY_CRC_DISCARD */
#if CONFIG_USB_PD_REV30
tcpc->tcpc_flags |= TCPC_FLAGS_PD_REV30;
if (tcpc->tcpc_flags & TCPC_FLAGS_PD_REV30)
dev_info(dev, "PD_REV30\n");
else
dev_info(dev, "PD_REV20\n");
#endif /* CONFIG_USB_PD_REV30 */
tcpc->tcpc_flags |= TCPC_FLAGS_ALERT_V10;
return 0;
}
static struct tcpc_ops upm7610_tcpc_ops = {
.init = upm7610_tcpc_init, /* type-c port control init upm7610端口控制器初始化 */
.alert_status_clear = upm7610_alert_status_clear, /* upm7610告警状态清除 */
.fault_status_clear = upm7610_fault_status_clear,
.set_alert_mask = upm7610_set_alert_mask, /* upm7610设置告警掩码 */
.get_alert_mask = upm7610_get_alert_mask,
.get_alert_status_and_mask = upm7610_get_alert_status_and_mask, /* upm7610获取告警状态和掩码 */
.get_power_status = upm7610_get_power_status, /* upm7610获取电源状态 */
.get_fault_status = upm7610_get_fault_status, /* upm7610获取错误状态 */
.get_cc = upm7610_get_cc, /* upm7610获取cc管脚状态 */
.set_cc = upm7610_set_cc, /* upm7610设置cc管脚状态 */
.set_polarity = upm7610_set_polarity, /* upm7610设置极性 */
.set_vconn = upm7610_set_vconn,
.deinit = upm7610_tcpc_deinit,
.set_low_power_mode = upm7610_set_low_power_mode, /* upm7610 设置低功耗电源模式 */
#if IS_ENABLED(CONFIG_USB_POWER_DELIVERY)
.set_msg_header = upm7610_set_msg_header, /* upm7610 设置消息头 */
.set_rx_enable = upm7610_set_rx_enable, /* upm7610 接收消息使能 */
.protocol_reset = NULL,
.get_message = upm7610_get_message, /* upm7610 获取消息 */
.transmit = upm7610_transmit, /* upm7610 传输消息 */
.set_bist_test_mode = upm7610_set_bist_test_mode, /* upm7610 自测模式 */
.set_bist_carrier_mode = upm7610_set_bist_carrier_mode, /* upm7610 获取负载函数 */
#endif /* CONFIG_USB_POWER_DELIVERY */
#if CONFIG_USB_PD_RETRY_CRC_DISCARD
.retransmit = upm7610_retransmit, /* 信息发送失败时重传函数 */
#endif /* CONFIG_USB_PD_RETRY_CRC_DISCARD */
};
4.解析upm7610_init_alert函数
定义一个中断线程,接受到中断后中断程序upm7610_intr_handler使用tcpci_alert(tcpci_alert.c)函数将信息同步到框架层。
static int upm7610_init_alert(struct tcpc_device *tcpc)
{
struct upm7610_chip *chip = tcpc_get_dev_data(tcpc);
int ret = 0;
char *name = NULL;
/* Clear Alert Mask & Status */
upm7610_write_word(chip->client, TCPC_V10_REG_ALERT_MASK, 0);
upm7610_write_word(chip->client, TCPC_V10_REG_ALERT, 0xffff);
name = devm_kasprintf(chip->dev, GFP_KERNEL, "%s-IRQ",
chip->tcpc_desc->name);
if (!name)
return -ENOMEM;
dev_info(chip->dev, "%s name = %s, gpio = %d\n",
__func__, chip->tcpc_desc->name, chip->irq_gpio);
ret = devm_gpio_request(chip->dev, chip->irq_gpio, name);
if (ret < 0) {
dev_notice(chip->dev, "%s request GPIO fail(%d)\n",
__func__, ret);
return ret;
}
ret = gpio_direction_input(chip->irq_gpio);
if (ret < 0) {
dev_notice(chip->dev, "%s set GPIO fail(%d)\n", __func__, ret);
return ret;
}
ret = gpio_to_irq(chip->irq_gpio);
if (ret < 0) {
dev_notice(chip->dev, "%s gpio to irq fail(%d)",
__func__, ret);
return ret;
}
chip->irq = ret;
dev_info(chip->dev, "%s IRQ number = %d\n", __func__, chip->irq);
device_init_wakeup(chip->dev, true);
ret = devm_request_threaded_irq(chip->dev, chip->irq, NULL,
upm7610_intr_handler,
IRQF_TRIGGER_LOW | IRQF_ONESHOT, /* 低电平触发中断 中断处理完成后才重新使能中断 */
name, chip);
if (ret < 0) {
dev_notice(chip->dev, "%s request irq fail(%d)\n",
__func__, ret);
return ret;
}
enable_irq_wake(chip->irq);
return 0;
}
static irqreturn_t upm7610_intr_handler(int irq, void *data)
{
struct upm7610_chip *chip = data;
mutex_lock(&chip->irq_lock);
if (chip->is_suspended) {
dev_notice(chip->dev, "%s irq while suspended\n", __func__);
chip->irq_while_suspended = true;
disable_irq_nosync(chip->irq);
mutex_unlock(&chip->irq_lock);
return IRQ_NONE;
}
mutex_unlock(&chip->irq_lock);
pm_stay_awake(chip->dev); /*通知电源管理子系统,防止系统进入 suspend 状态*/
tcpci_lock_typec(chip->tcpc);
tcpci_alert(chip->tcpc, false); /* 告警:处理 Type-C 相关的中断事件(如连接检测、PD 通信等)*/
tcpci_unlock_typec(chip->tcpc);
pm_relax(chip->dev);
return IRQ_HANDLED;
}
5总结
****所以pd驱动中基于pd框架代码主要通过tcpci_alert和tcpc_device_register与框架层交互,去理解Type-C port control interface框架层
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐

所有评论(0)