/* * Copyright (C) 2016 MediaTek Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See http://www.gnu.org/licenses/gpl-2.0.html for more details. */ #include /* For init/exit macros */ #include /* For MODULE_ marcros */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mtk_charger_intf.h" #include #include #include #include /*irq_to_desc*/ #include #include /* PD */ #include int nr_num; struct adapter_power_cap ta_cap = {0}; struct power_supply *usb_psy = NULL; union power_supply_propval val; int get_apdo_regain; enum pd_type_def { PD_SRC_PDO_TYPE_FIXED, PD_SRC_PDO_TYPE_PPS, PD_SRC_PDO_TYPE_UNKNOW, }; struct mtk_pd_adapter_info { struct tcpc_device *tcpc; struct notifier_block pd_nb; struct adapter_device *adapter_dev; struct task_struct *adapter_task; const char *adapter_dev_name; bool enable_kpoc_shdn; bool non_mtk_pps_ctr; struct tcpm_svid_list *adapter_svid_list; }; static int pd_tcp_notifier_call(struct notifier_block *pnb, unsigned long event, void *data) { struct tcp_notify *noti = data; struct mtk_pd_adapter_info *pinfo; pinfo = container_of(pnb, struct mtk_pd_adapter_info, pd_nb); chr_err("PD charger event:%d %d\n", (int)event, (int)noti->pd_state.connected); switch (event) { case TCP_NOTIFY_PD_STATE: switch (noti->pd_state.connected) { case PD_CONNECT_NONE: pinfo->adapter_dev->adapter_id = 0; pinfo->adapter_dev->adapter_svid = 0; pinfo->adapter_dev->uvdm_state = USBPD_UVDM_DISCONNECT; pinfo->adapter_dev->verifed = 0; pinfo->adapter_dev->verify_process = 0; notify_adapter_event(MTK_PD_ADAPTER, MTK_PD_CONNECT_NONE, NULL); break; case PD_CONNECT_HARD_RESET: notify_adapter_event(MTK_PD_ADAPTER, MTK_PD_CONNECT_HARD_RESET, NULL); break; case PD_CONNECT_PE_READY_SNK: notify_adapter_event(MTK_PD_ADAPTER, MTK_PD_CONNECT_PE_READY_SNK, NULL); break; case PD_CONNECT_PE_READY_SNK_PD30: pinfo->adapter_dev->uvdm_state = USBPD_UVDM_CONNECT; notify_adapter_event(MTK_PD_ADAPTER, MTK_PD_CONNECT_PE_READY_SNK_PD30, NULL); break; case PD_CONNECT_PE_READY_SNK_APDO: pinfo->adapter_dev->uvdm_state = USBPD_UVDM_CONNECT; get_apdo_regain = 1; notify_adapter_event(MTK_PD_ADAPTER, MTK_PD_CONNECT_PE_READY_SNK_APDO, NULL); break; case PD_CONNECT_TYPEC_ONLY_SNK: notify_adapter_event(MTK_PD_ADAPTER, MTK_PD_CONNECT_TYPEC_ONLY_SNK, NULL); break; }; break; case TCP_NOTIFY_WD_STATUS: notify_adapter_event(MTK_PD_ADAPTER, MTK_TYPEC_WD_STATUS, ¬i->wd_status.water_detected); break; case TCP_NOTIFY_HARD_RESET_STATE: if (noti->hreset_state.state == TCP_HRESET_RESULT_DONE || noti->hreset_state.state == TCP_HRESET_RESULT_FAIL) { pinfo->enable_kpoc_shdn = true; notify_adapter_event(MTK_PD_ADAPTER, MTK_TYPEC_HRESET_STATUS, &pinfo->enable_kpoc_shdn); } else if (noti->hreset_state.state == TCP_HRESET_SIGNAL_SEND || noti->hreset_state.state == TCP_HRESET_SIGNAL_RECV) { pinfo->enable_kpoc_shdn = false; notify_adapter_event(MTK_PD_ADAPTER, MTK_TYPEC_HRESET_STATUS, &pinfo->enable_kpoc_shdn); } break; case TCP_NOTIFY_UVDM: pr_info("%s: tcpc received uvdm message.\n", __func__); notify_adapter_event(MTK_PD_ADAPTER, MTK_PD_UVDM, ¬i->uvdm_msg); break; } return NOTIFY_OK; } static int pd_get_property(struct adapter_device *dev, enum adapter_property sta) { struct mtk_pd_adapter_info *info; info = (struct mtk_pd_adapter_info *)adapter_dev_get_drvdata(dev); if (info == NULL || info->tcpc == NULL) return -1; switch (sta) { case TYPEC_RP_LEVEL: { return tcpm_inquire_typec_remote_rp_curr(info->tcpc); } break; default: { } break; } return -1; } int find_pd_type(int mV, int mA) { uint8_t i = 0; chr_err("[%s] select pdo is mV[%d] mA[%d]\n", __func__, mV, mA); chr_err("[%s] ta_cap.nr is %d\n", __func__, ta_cap.nr); chr_err("[%s] nr_num is %d\n", __func__, nr_num); for (i = 0; i < ta_cap.nr; i++) { chr_err("[%s] TA value is [%d %d %d]\n", __func__, ta_cap.min_mv[i], ta_cap.max_mv[i], ta_cap.ma[i]); if ((mV == ta_cap.min_mv[i]) && (mV == ta_cap.max_mv[i]) && (mA == ta_cap.ma[i])) { chr_err("[%s] select value is fixed.\n", __func__); return PD_SRC_PDO_TYPE_FIXED; } else if ((mV >= ta_cap.min_mv[i]) && (mV <= ta_cap.max_mv[i]) && (mA <= ta_cap.ma[i])) { chr_err("[%s] select value is pps.\n", __func__); return PD_SRC_PDO_TYPE_PPS; } } chr_err("[%s] select value is not right.\n", __func__); return PD_SRC_PDO_TYPE_UNKNOW; } static int pd_set_cap(struct adapter_device *dev, enum adapter_cap_type type, int mV, int mA) { int ret = MTK_ADAPTER_OK; int tcpm_ret = TCPM_SUCCESS; struct mtk_pd_adapter_info *info = NULL; chr_err("[%s] type:%d mV:%d mA:%d\n", __func__, type, mV, mA); info = (struct mtk_pd_adapter_info *)adapter_dev_get_drvdata(dev); if (info == NULL || info->tcpc == NULL) { chr_err("[%s] info null\n", __func__); return -1; } if (info->adapter_dev->verify_process) return -1; /* use xiaomi pps control state machine */ if (info->non_mtk_pps_ctr) { if (find_pd_type(mV, mA) == PD_SRC_PDO_TYPE_PPS) { chr_err("[%s] PPS is controlled by ourself, return not support\n", __func__); return -1; } } if (type == MTK_PD_APDO_START) { tcpm_ret = tcpm_set_apdo_charging_policy(info->tcpc, DPM_CHARGING_POLICY_PPS, mV, mA, NULL); } else if (type == MTK_PD_APDO_END) { tcpm_ret = tcpm_set_pd_charging_policy(info->tcpc, DPM_CHARGING_POLICY_VSAFE5V, NULL); } else if (type == MTK_PD_APDO) { tcpm_ret = tcpm_dpm_pd_request(info->tcpc, mV, mA, NULL); } else if (type == MTK_PD) { tcpm_ret = tcpm_dpm_pd_request(info->tcpc, mV, mA, NULL); } chr_err("[%s] type:%d mV:%d mA:%d ret:%d\n", __func__, type, mV, mA, tcpm_ret); if (tcpm_ret == TCP_DPM_RET_REJECT) return MTK_ADAPTER_REJECT; else if (tcpm_ret != 0) return MTK_ADAPTER_ERROR; return ret; } static int pd_set_cap_bq(struct adapter_device *dev, enum adapter_cap_type type, int mV, int mA) { int ret = MTK_ADAPTER_OK; int tcpm_ret = TCPM_SUCCESS; struct mtk_pd_adapter_info *info; chr_err("[%s] type:%d mV:%d mA:%d\n", __func__, type, mV, mA); info = (struct mtk_pd_adapter_info *)adapter_dev_get_drvdata(dev); if (info == NULL || info->tcpc == NULL) { chr_err("[%s] info null\n", __func__); return -1; } if (info->adapter_dev->verify_process) return -1; if (type == MTK_PD_APDO_START) { tcpm_ret = tcpm_set_apdo_charging_policy(info->tcpc, DPM_CHARGING_POLICY_PPS, mV, mA, NULL); } else if (type == MTK_PD_APDO_END) { tcpm_ret = tcpm_set_pd_charging_policy(info->tcpc, DPM_CHARGING_POLICY_VSAFE5V, NULL); } else if (type == MTK_PD_APDO) { tcpm_ret = tcpm_dpm_pd_request(info->tcpc, mV, mA, NULL); } else if (type == MTK_PD) { tcpm_ret = tcpm_dpm_pd_request(info->tcpc, mV, mA, NULL); } chr_err("[%s] type:%d mV:%d mA:%d ret:%d\n", __func__, type, mV, mA, tcpm_ret); if (tcpm_ret == TCP_DPM_RET_REJECT) return MTK_ADAPTER_REJECT; else if (tcpm_ret != 0) return MTK_ADAPTER_ERROR; return ret; } static int pd_set_pd_verify_process(struct adapter_device *dev, int verify_in_process) { int ret = 0; union power_supply_propval val = {0,}; chr_err("[%s] pd verify in process:%d\n", __func__, verify_in_process); if (usb_psy) { val.intval = verify_in_process; ret = power_supply_set_property(usb_psy, POWER_SUPPLY_PROP_PD_VERIFY_IN_PROCESS, &val); } else { chr_err("[%s] usb psy not found!\n", __func__); } return ret; } int pd_get_output(struct adapter_device *dev, int *mV, int *mA) { int ret = MTK_ADAPTER_OK; int tcpm_ret = TCPM_SUCCESS; struct pd_pps_status pps_status; struct mtk_pd_adapter_info *info; info = (struct mtk_pd_adapter_info *)adapter_dev_get_drvdata(dev); if (info == NULL || info->tcpc == NULL) return MTK_ADAPTER_NOT_SUPPORT; tcpm_ret = tcpm_dpm_pd_get_pps_status(info->tcpc, NULL, &pps_status); if (tcpm_ret == TCP_DPM_RET_NOT_SUPPORT) return MTK_ADAPTER_NOT_SUPPORT; else if (tcpm_ret != 0) return MTK_ADAPTER_ERROR; *mV = pps_status.output_mv; *mA = pps_status.output_ma; return ret; } int pd_get_status(struct adapter_device *dev, struct adapter_status *sta) { struct pd_status TAstatus = {0,}; int ret = MTK_ADAPTER_OK; int tcpm_ret = TCPM_SUCCESS; struct mtk_pd_adapter_info *info; info = (struct mtk_pd_adapter_info *)adapter_dev_get_drvdata(dev); if (info == NULL || info->tcpc == NULL) return MTK_ADAPTER_ERROR; tcpm_ret = tcpm_dpm_pd_get_status(info->tcpc, NULL, &TAstatus); sta->temperature = TAstatus.internal_temp; sta->ocp = TAstatus.event_flags & PD_STASUS_EVENT_OCP; sta->otp = TAstatus.event_flags & PD_STATUS_EVENT_OTP; sta->ovp = TAstatus.event_flags & PD_STATUS_EVENT_OVP; if (tcpm_ret == TCP_DPM_RET_NOT_SUPPORT) return MTK_ADAPTER_NOT_SUPPORT; else if (tcpm_ret == TCP_DPM_RET_TIMEOUT) return MTK_ADAPTER_TIMEOUT; else if (tcpm_ret == TCP_DPM_RET_SUCCESS) return MTK_ADAPTER_OK; else return MTK_ADAPTER_ERROR; return ret; } static int pd_get_cap(struct adapter_device *dev, enum adapter_cap_type type, struct adapter_power_cap *tacap) { struct tcpm_power_cap_val apdo_cap; struct tcpm_remote_power_cap pd_cap; struct pd_source_cap_ext cap_ext; uint8_t cap_i = 0; int ret; int idx = 0; int i; int timeout = 0; struct mtk_pd_adapter_info *info; info = (struct mtk_pd_adapter_info *)adapter_dev_get_drvdata(dev); if (info == NULL || info->tcpc == NULL) return MTK_ADAPTER_ERROR; if (info->adapter_dev->verify_process) return -1; if (type == MTK_PD_APDO) { while (1) { ret = tcpm_inquire_pd_source_apdo(info->tcpc, TCPM_POWER_CAP_APDO_TYPE_PPS, &cap_i, &apdo_cap); if (ret == TCPM_ERROR_NOT_FOUND) { break; } else if (ret != TCPM_SUCCESS) { chr_err("[%s] tcpm_inquire_pd_source_apdo failed(%d)\n", __func__, ret); break; } ret = tcpm_dpm_pd_get_source_cap_ext(info->tcpc, NULL, &cap_ext); if (ret == TCPM_SUCCESS) tacap->pdp = cap_ext.source_pdp; else { tacap->pdp = 0; chr_err("[%s] tcpm_dpm_pd_get_source_cap_ext failed(%d)\n", __func__, ret); } tacap->pwr_limit[idx] = apdo_cap.pwr_limit; tacap->ma[idx] = apdo_cap.ma; tacap->max_mv[idx] = apdo_cap.max_mv; tacap->min_mv[idx] = apdo_cap.min_mv; tacap->maxwatt[idx] = apdo_cap.max_mv * apdo_cap.ma; tacap->minwatt[idx] = apdo_cap.min_mv * apdo_cap.ma; tacap->type[idx] = MTK_PD_APDO; idx++; chr_err("pps_boundary[%d], %d mv ~ %d mv, %d ma pl:%d\n", cap_i, apdo_cap.min_mv, apdo_cap.max_mv, apdo_cap.ma, apdo_cap.pwr_limit); if (idx >= ADAPTER_CAP_MAX_NR) { chr_err("CAP NR > %d\n", ADAPTER_CAP_MAX_NR); break; } } tacap->nr = idx; for (i = 0; i < tacap->nr; i++) { chr_err("pps_cap[%d:%d], %d mv ~ %d mv, %d ma pl:%d pdp:%d\n", i, (int)tacap->nr, tacap->min_mv[i], tacap->max_mv[i], tacap->ma[i], tacap->pwr_limit[i], tacap->pdp); } if (cap_i == 0) chr_err("no APDO for pps\n"); } else if (type == MTK_PD) { APDO_REGAIN: pd_cap.nr = 0; pd_cap.selected_cap_idx = 0; tcpm_get_remote_power_cap(info->tcpc, &pd_cap); tacap->nr = pd_cap.nr; tacap->selected_cap_idx = pd_cap.selected_cap_idx - 1; chr_err("[%s] nr:%d idx:%d\n", __func__, pd_cap.nr, pd_cap.selected_cap_idx - 1); for (i = 0; i < pd_cap.nr; i++) { tacap->ma[i] = pd_cap.ma[i]; tacap->max_mv[i] = pd_cap.max_mv[i]; tacap->min_mv[i] = pd_cap.min_mv[i]; tacap->maxwatt[i] = tacap->max_mv[i] * tacap->ma[i]; if (pd_cap.type[i] == 0) tacap->type[i] = MTK_PD; else if (pd_cap.type[i] == 3) tacap->type[i] = MTK_PD_APDO; else tacap->type[i] = MTK_CAP_TYPE_UNKNOWN; tacap->type[i] = pd_cap.type[i]; chr_err("[%s]:%d mv:[%d,%d] %d max:%d min:%d type:%d %d\n", __func__, i, tacap->min_mv[i], tacap->max_mv[i], tacap->ma[i], tacap->maxwatt[i], tacap->minwatt[i], tacap->type[i], pd_cap.type[i]); } } else if (type == MTK_PD_APDO_REGAIN) { get_apdo_regain = 0; ret = tcpm_dpm_pd_get_source_cap(info->tcpc, NULL); if (ret == TCPM_SUCCESS) { while (timeout < 10) { if (get_apdo_regain) { chr_err("[%s] ready to get pps info!\n", __func__); goto APDO_REGAIN; } else { msleep(100); timeout++; } } chr_err("[%s] ready to get pps info - for test!\n", __func__); goto APDO_REGAIN; } else { chr_err("[%s] tcpm_dpm_pd_get_source_cap failed!\n", __func__); return MTK_ADAPTER_ERROR; } } ta_cap.nr = tacap->nr; for (i = 0; i < tacap->nr; i++) { ta_cap.max_mv[i] = tacap->max_mv[i]; ta_cap.min_mv[i] = tacap->min_mv[i]; ta_cap.ma[i] = tacap->ma[i]; if (tacap->type[i] == 3) val.intval = POWER_SUPPLY_PD_PPS_ACTIVE; if ((tacap->type[i] == 0) && (val.intval != POWER_SUPPLY_PD_PPS_ACTIVE)) val.intval = POWER_SUPPLY_PD_ACTIVE; } if (ta_cap.nr) { usb_psy = power_supply_get_by_name("usb"); if (usb_psy) { power_supply_set_property(usb_psy, POWER_SUPPLY_PROP_PD_ACTIVE, &val); } else { chr_err("[%s] usb psy not found!\n", __func__); } } nr_num = ta_cap.nr; chr_err("[%s] ta_cap.nr is %d\n", __func__, ta_cap.nr); chr_err("[%s] nr_num is %d\n", __func__, nr_num); return MTK_ADAPTER_OK; } static int pd_get_svid(struct adapter_device *dev) { struct mtk_pd_adapter_info *info; struct pd_source_cap_ext cap_ext; int ret; int i = 0; uint32_t pd_vdos[8]; info = (struct mtk_pd_adapter_info *)adapter_dev_get_drvdata(dev); if (info == NULL) return MTK_ADAPTER_ERROR; pr_info("%s: enter\n", __func__); if (info->adapter_dev->adapter_svid != 0) return MTK_ADAPTER_OK; if (info->adapter_svid_list == NULL) { if (in_interrupt()) { info->adapter_svid_list = kmalloc(sizeof(struct tcpm_svid_list), GFP_ATOMIC); } else { info->adapter_svid_list = kmalloc(sizeof(struct tcpm_svid_list), GFP_KERNEL); } if (info->adapter_svid_list == NULL) chr_err("[%s] adapter_svid_list is still NULL!\n", __func__); } ret = tcpm_inquire_pd_partner_inform(info->tcpc, pd_vdos); if (ret == TCPM_SUCCESS) { pr_info("find adapter id success.\n"); for (i = 0; i < 8; i++) pr_info("VDO[%d] : %08x\n", i, pd_vdos[i]); info->adapter_dev->adapter_svid = pd_vdos[0] & 0x0000FFFF; info->adapter_dev->adapter_id = pd_vdos[2] & 0x0000FFFF; pr_info("adapter_svid = %04x\n", info->adapter_dev->adapter_svid); pr_info("adapter_id = %08x\n", info->adapter_dev->adapter_id); ret = tcpm_inquire_pd_partner_svids(info->tcpc, info->adapter_svid_list); pr_info("[%s] tcpm_inquire_pd_partner_svids, ret=%d!\n", __func__, ret); if (ret == TCPM_SUCCESS) { pr_info("discover svid number is %d\n", info->adapter_svid_list->cnt); for (i = 0; i < info->adapter_svid_list->cnt; i++) { pr_info("SVID[%d] : %04x\n", i, info->adapter_svid_list->svids[i]); if (info->adapter_svid_list->svids[i] == USB_PD_MI_SVID) info->adapter_dev->adapter_svid = USB_PD_MI_SVID; } } } else { ret = tcpm_dpm_pd_get_source_cap_ext(info->tcpc, NULL, &cap_ext); if (ret == TCPM_SUCCESS) { info->adapter_dev->adapter_svid = cap_ext.vid & 0x0000FFFF; info->adapter_dev->adapter_id = cap_ext.pid & 0x0000FFFF; info->adapter_dev->adapter_fw_ver = cap_ext.fw_ver & 0x0000FFFF; info->adapter_dev->adapter_hw_ver = cap_ext.hw_ver & 0x0000FFFF; pr_info("adapter_svid = %04x\n", info->adapter_dev->adapter_svid); pr_info("adapter_id = %08x\n", info->adapter_dev->adapter_id); pr_info("adapter_fw_ver = %08x\n", info->adapter_dev->adapter_fw_ver); pr_info("adapter_hw_ver = %08x\n", info->adapter_dev->adapter_hw_ver); } else { chr_err("[%s] get adapter message failed!\n", __func__); return MTK_ADAPTER_ERROR; } } return MTK_ADAPTER_OK; } #if 0 static int pd_get_svid(struct adapter_device *dev) { struct mtk_pd_adapter_info *info; struct pd_source_cap_ext cap_ext; int ret; int i = 0; uint32_t pd_vdos[8]; info = (struct mtk_pd_adapter_info *)adapter_dev_get_drvdata(dev); if (info == NULL) return MTK_ADAPTER_ERROR; pr_info("%s: enter\n", __func__); if (info->adapter_dev->adapter_svid != 0) return MTK_ADAPTER_OK; ret = tcpm_inquire_pd_partner_inform(info->tcpc, pd_vdos); if (ret == TCPM_SUCCESS) { pr_info("find adapter id success.\n"); for (i = 0; i < 8; i++) pr_info("VDO[%d] : %08x\n", i, pd_vdos[i]); info->adapter_dev->adapter_svid = pd_vdos[0] & 0x0000FFFF; info->adapter_dev->adapter_id = pd_vdos[2]; pr_info("adapter_svid = %04x\n", info->adapter_dev->adapter_svid); pr_info("adapter_id = %08x\n", info->adapter_dev->adapter_id); } ret = tcpm_dpm_pd_get_source_cap_ext(info->tcpc, NULL, &cap_ext); if (ret == TCPM_SUCCESS) { info->adapter_dev->adapter_svid = cap_ext.vid & 0x0000FFFF; info->adapter_dev->adapter_id = cap_ext.pid & 0x0000FFFF; info->adapter_dev->adapter_fw_ver = cap_ext.fw_ver & 0x0000FFFF; info->adapter_dev->adapter_hw_ver = cap_ext.hw_ver & 0x0000FFFF; pr_info("adapter_svid = %04x\n", info->adapter_dev->adapter_svid); pr_info("adapter_id = %08x\n", info->adapter_dev->adapter_id); pr_info("adapter_fw_ver = %08x\n", info->adapter_dev->adapter_fw_ver); pr_info("adapter_hw_ver = %08x\n", info->adapter_dev->adapter_hw_ver); } else { chr_err("[%s] get adapter message failed!\n", __func__); //return MTK_ADAPTER_ERROR; } if (info->adapter_svid_list == NULL) { chr_err("[%s] adapter_svid_list is NULL!\n", __func__); if (in_interrupt()) { info->adapter_svid_list = kmalloc(sizeof(struct tcpm_svid_list), GFP_ATOMIC); } else { info->adapter_svid_list = kmalloc(sizeof(struct tcpm_svid_list), GFP_KERNEL); } if (info->adapter_svid_list == NULL) chr_err("[%s] adapter_svid_list is still NULL!\n", __func__); } ret = tcpm_inquire_pd_partner_svids(info->tcpc, info->adapter_svid_list); if (ret == TCPM_SUCCESS) { pr_info("discover svid number is %d\n", info->adapter_svid_list->cnt); for (i = 0; i < info->adapter_svid_list->cnt; i++) { pr_info("SVID[%d] : %04x\n", i, info->adapter_svid_list->svids[i]); if (info->adapter_svid_list->svids[i] == USB_PD_MI_SVID) info->adapter_dev->adapter_svid = USB_PD_MI_SVID; } } chr_err("[%s] tcpm_inquire_pd_partner_svids ret = %d\n", __func__, ret); return MTK_ADAPTER_OK; } #endif #if 0 static int pd_get_id(struct adapter_device *dev) { struct mtk_pd_adapter_info *info; int ret; int i = 0; uint32_t pd_vdos[8]; info = (struct mtk_pd_adapter_info *)adapter_dev_get_drvdata(dev); if (info == NULL) return MTK_ADAPTER_ERROR; ret = tcpm_inquire_pd_partner_inform(info->tcpc, pd_vdos); if (ret == TCPM_SUCCESS) { pr_info("find adapter id success.\n"); for (i = 0; i < 8; i++) pr_info("VDO[%d] : %08x\n", i, pd_vdos[i]); info->adapter_dev->adapter_svid = pd_vdos[0] & 0x0000FFFF; info->adapter_dev->adapter_id = pd_vdos[2]; pr_info("adapter_svid = %04x\n", info->adapter_dev->adapter_svid); pr_info("adapter_id = %08x\n", info->adapter_dev->adapter_id); } return MTK_ADAPTER_OK; } #endif #define BSWAP_32(x) \ (u32)((((u32)(x) & 0xff000000) >> 24) | \ (((u32)(x) & 0x00ff0000) >> 8) | \ (((u32)(x) & 0x0000ff00) << 8) | \ (((u32)(x) & 0x000000ff) << 24)) static void usbpd_sha256_bitswap32(unsigned int *array, int len) { int i; for (i = 0; i < len; i++) array[i] = BSWAP_32(array[i]); } void charToint(char *str, int input_len, unsigned int *out, unsigned int *outlen) { int i; if (outlen != NULL) *outlen = 0; for (i = 0; i < (input_len / 4 + 1); i++) { out[i] = ((str[i*4 + 3] * 0x1000000) | (str[i*4 + 2] * 0x10000) | (str[i*4 + 1] * 0x100) | str[i*4]); *outlen = *outlen + 1; } pr_info("%s: outlen = %d\n", __func__, *outlen); for (i = 0; i < *outlen; i++) pr_info("%s: out[%d] = %08x\n", __func__, i, out[i]); pr_info("%s: char to int done.\n", __func__); } static int tcp_dpm_event_cb_uvdm(struct tcpc_device *tcpc, int ret, struct tcp_dpm_event *event) { int i; struct tcp_dpm_custom_vdm_data vdm_data = event->tcp_dpm_data.vdm_data; pr_info("%s: vdm_data.cnt = %d\n", __func__, vdm_data.cnt); for (i = 0; i < vdm_data.cnt; i++) chr_info("%s vdm_data.vdos[%d] = 0x%08x", __func__, i, vdm_data.vdos[i]); return 0; } const struct tcp_dpm_event_cb_data cb_data = { .event_cb = tcp_dpm_event_cb_uvdm, }; static int pd_request_vdm_cmd(struct adapter_device *dev, enum uvdm_state cmd, unsigned char *data, unsigned int data_len) { u32 vdm_hdr = 0; int rc = 0; struct tcp_dpm_custom_vdm_data *vdm_data; struct mtk_pd_adapter_info *info; unsigned int *int_data; unsigned int outlen; // test int i; // test if (in_interrupt()) { int_data = kmalloc(40, GFP_ATOMIC); vdm_data = kmalloc(sizeof(*vdm_data), GFP_ATOMIC); pr_info("%s: kmalloc atomic ok.\n", __func__); } else { int_data = kmalloc(40, GFP_KERNEL); vdm_data = kmalloc(sizeof(*vdm_data), GFP_KERNEL); pr_info("%s: kmalloc kernel ok.\n", __func__); } memset(int_data, 0, 40); charToint(data, data_len, int_data, &outlen); info = (struct mtk_pd_adapter_info *)adapter_dev_get_drvdata(dev); if (info == NULL || info->tcpc == NULL) return MTK_ADAPTER_ERROR; vdm_hdr = VDM_HDR(info->adapter_dev->adapter_svid, USBPD_VDM_REQUEST, cmd); vdm_data->wait_resp = true; vdm_data->vdos[0] = vdm_hdr; switch (cmd) { case USBPD_UVDM_CHARGER_VERSION: case USBPD_UVDM_CHARGER_TEMP: case USBPD_UVDM_CHARGER_VOLTAGE: vdm_data->cnt = 1; rc = tcpm_dpm_send_custom_vdm(info->tcpc, vdm_data, &cb_data);//&tcp_dpm_evt_cb_null if (rc < 0) { chr_err("failed to send %d\n", cmd); return rc; } break; case USBPD_UVDM_VERIFIED: case USBPD_UVDM_REMOVE_COMPENSATION: if (info->non_mtk_pps_ctr) { if (cmd == USBPD_UVDM_REMOVE_COMPENSATION) break; } vdm_data->cnt = 1 + USBPD_UVDM_VERIFIED_LEN; for (i = 0; i < USBPD_UVDM_VERIFIED_LEN; i++) vdm_data->vdos[i + 1] = int_data[i]; pr_info("verify-0: %08x\n", vdm_data->vdos[1]); rc = tcpm_dpm_send_custom_vdm(info->tcpc, vdm_data, &cb_data);//&tcp_dpm_evt_cb_null if (rc < 0) { chr_err("failed to send %d\n", cmd); return rc; } break; case USBPD_UVDM_SESSION_SEED: case USBPD_UVDM_AUTHENTICATION: case USBPD_UVDM_REVERSE_AUTHEN: usbpd_sha256_bitswap32(int_data, USBPD_UVDM_SS_LEN); vdm_data->cnt = 1 + USBPD_UVDM_SS_LEN; for (i = 0; i < USBPD_UVDM_SS_LEN; i++) vdm_data->vdos[i + 1] = int_data[i]; for (i = 0; i < USBPD_UVDM_SS_LEN; i++) pr_info("%08x\n", vdm_data->vdos[i+1]); rc = tcpm_dpm_send_custom_vdm(info->tcpc, vdm_data, &cb_data);//&tcp_dpm_evt_cb_null if (rc < 0) { chr_err("failed to send %d\n", cmd); return rc; } break; default: chr_err("cmd:%d is not support\n", cmd); break; } kfree(int_data); return rc; } static int pd_get_power_role(struct adapter_device *dev) { struct mtk_pd_adapter_info *info; info = (struct mtk_pd_adapter_info *)adapter_dev_get_drvdata(dev); if (info == NULL || info->tcpc == NULL) return MTK_ADAPTER_ERROR; info->adapter_dev->role = tcpm_inquire_pd_power_role(info->tcpc); chr_err("[%s] power role is %d\n", __func__, info->adapter_dev->role); return MTK_ADAPTER_OK; } static int pd_get_current_state(struct adapter_device *dev) { struct mtk_pd_adapter_info *info; info = (struct mtk_pd_adapter_info *)adapter_dev_get_drvdata(dev); if (info == NULL || info->tcpc == NULL) return MTK_ADAPTER_ERROR; info->adapter_dev->current_state = tcpm_inquire_pd_state_curr(info->tcpc); chr_err("[%s] current state is %d\n", __func__, info->adapter_dev->current_state); return MTK_ADAPTER_OK; } static int pd_get_pdos(struct adapter_device *dev) { struct mtk_pd_adapter_info *info; struct tcpm_power_cap cap; int ret, i; info = (struct mtk_pd_adapter_info *)adapter_dev_get_drvdata(dev); if (info == NULL || info->tcpc == NULL) return MTK_ADAPTER_ERROR; #if 0 info->adapter_dev->role = tcpm_inquire_pd_power_role(info->tcpc); chr_err("[%s] power role is %d\n", __func__, info->adapter_dev->role); if (info->adapter_dev->role == PD_ROLE_SINK_FOR_ADAPTER) { ret = tcpm_inquire_pd_sink_cap(info->tcpc, &cap); chr_err("[%s] tcpm_inquire_pd_sink_cap is %d.\n", __func__, ret); } else if (info->adapter_dev->role == PD_ROLE_SOURCE_FOR_ADAPTER) { ret = tcpm_inquire_pd_source_cap(info->tcpc, &cap); chr_err("[%s] tcpm_inquire_pd_source_cap is %d.\n", __func__, ret); } #endif ret = tcpm_inquire_pd_source_cap(info->tcpc, &cap); chr_err("[%s] tcpm_inquire_pd_source_cap is %d.\n", __func__, ret); if (ret) return MTK_ADAPTER_ERROR; for (i = 0; i < 7; i++) { info->adapter_dev->received_pdos[i] = cap.pdos[i]; chr_err("[%s]: pdo[%d] { received_pdos is %08x, cap.pdos is %08x}\n", __func__, i, info->adapter_dev->received_pdos[i], cap.pdos[i]); } return MTK_ADAPTER_OK; } static struct adapter_ops adapter_ops = { .get_status = pd_get_status, .set_cap = pd_set_cap, .get_output = pd_get_output, .get_property = pd_get_property, .get_cap = pd_get_cap, .set_cap_bq = pd_set_cap_bq, .get_svid = pd_get_svid, //.get_id = pd_get_id, .request_vdm_cmd = pd_request_vdm_cmd, .get_power_role = pd_get_power_role, .get_current_state = pd_get_current_state, .get_pdos = pd_get_pdos, .set_pd_verify_process = pd_set_pd_verify_process, }; static int adapter_parse_dt(struct mtk_pd_adapter_info *info, struct device *dev) { struct device_node *np = dev->of_node; chr_err("%s\n", __func__); if (!np) { chr_err("%s: no device node\n", __func__); return -EINVAL; } if (of_property_read_string(np, "adapter_name", &info->adapter_dev_name) < 0) chr_err("%s: no adapter name\n", __func__); info->non_mtk_pps_ctr = of_property_read_bool(np, "mi,non-mtk-pps-ctrl"); return 0; } static int mtk_pd_adapter_probe(struct platform_device *pdev) { int ret = 0; struct mtk_pd_adapter_info *info = NULL; static bool is_deferred; chr_err("%s\n", __func__); info = devm_kzalloc(&pdev->dev, sizeof(struct mtk_pd_adapter_info), GFP_KERNEL); if (!info) return -ENOMEM; adapter_parse_dt(info, &pdev->dev); info->adapter_dev = adapter_device_register(info->adapter_dev_name, &pdev->dev, info, &adapter_ops, NULL); if (IS_ERR_OR_NULL(info->adapter_dev)) { ret = PTR_ERR(info->adapter_dev); goto err_register_adapter_dev; } adapter_dev_set_drvdata(info->adapter_dev, info); info->tcpc = tcpc_dev_get_by_name("type_c_port0"); if (info->tcpc == NULL) { if (is_deferred == false) { pr_info("%s: tcpc device not ready, defer\n", __func__); is_deferred = true; ret = -EPROBE_DEFER; } else { pr_info("%s: failed to get tcpc device\n", __func__); ret = -EINVAL; } goto err_get_tcpc_dev; } info->pd_nb.notifier_call = pd_tcp_notifier_call; ret = register_tcp_dev_notifier(info->tcpc, &info->pd_nb, TCP_NOTIFY_TYPE_USB | TCP_NOTIFY_TYPE_MISC | TCP_NOTIFY_TYPE_MODE); if (ret < 0) { pr_info("%s: register tcpc notifer fail\n", __func__); ret = -EINVAL; goto err_get_tcpc_dev; } usb_psy = power_supply_get_by_name("usb"); if (usb_psy) { val.intval = POWER_SUPPLY_PD_INACTIVE; power_supply_set_property(usb_psy, POWER_SUPPLY_PROP_PD_ACTIVE, &val); } else { chr_err("[%s] usb psy not found!\n", __func__); } return 0; err_get_tcpc_dev: adapter_device_unregister(info->adapter_dev); err_register_adapter_dev: devm_kfree(&pdev->dev, info); return ret; } static int mtk_pd_adapter_remove(struct platform_device *dev) { return 0; } static void mtk_pd_adapter_shutdown(struct platform_device *dev) { } static const struct of_device_id mtk_pd_adapter_of_match[] = { {.compatible = "mediatek,pd_adapter",}, {}, }; MODULE_DEVICE_TABLE(of, mtk_pd_adapter_of_match); static struct platform_driver mtk_pd_adapter_driver = { .probe = mtk_pd_adapter_probe, .remove = mtk_pd_adapter_remove, .shutdown = mtk_pd_adapter_shutdown, .driver = { .name = "pd_adapter", .of_match_table = mtk_pd_adapter_of_match, }, }; static int __init mtk_pd_adapter_init(void) { return platform_driver_register(&mtk_pd_adapter_driver); } module_init(mtk_pd_adapter_init); static void __exit mtk_pd_adapter_exit(void) { platform_driver_unregister(&mtk_pd_adapter_driver); } module_exit(mtk_pd_adapter_exit); MODULE_AUTHOR("wy.chuang "); MODULE_DESCRIPTION("MTK PD Adapter Driver"); MODULE_LICENSE("GPL");