Files
android/drivers/power/supply/mediatek/charger/mtk_pd_adapter.c
T
threeeye bc1d038eac REVVL 6 Pro kernel bring-up: full working tree snapshot (2026-09-12)
Squashed backup commit (no shallow-clone history issues) of
xiaomi-mt6833-dev/kernel_xiaomi_mt6833 (lineage-23.2 branch, upstream
commit 9eece662e) plus all fixes applied to get everpal_defconfig building
clean against a modern (GCC 15.2.0) toolchain -- see the previous commit
message on the lineage-23.2 branch (also in this repo history is not
available here since this is a squashed snapshot; full incremental history
lives in the local DL380 checkout and the upstream GitHub remote).

Upstream: https://github.com/xiaomi-mt6833-dev/kernel_xiaomi_mt6833 (lineage-23.2)
2026-09-12 13:36:46 +00:00

1053 lines
29 KiB
C

/*
* 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 <linux/init.h> /* For init/exit macros */
#include <linux/module.h> /* For MODULE_ marcros */
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/platform_device.h>
#include <linux/device.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/wait.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/sched.h>
#include <linux/poll.h>
#include <linux/power_supply.h>
#include <linux/pm_wakeup.h>
#include <linux/time.h>
#include <linux/mutex.h>
#include <linux/kthread.h>
#include <linux/proc_fs.h>
#include <linux/platform_device.h>
#include <linux/seq_file.h>
#include <linux/scatterlist.h>
#include <linux/suspend.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include <linux/reboot.h>
#include <linux/power_supply.h>
#include "mtk_charger_intf.h"
#include <linux/stat.h>
#include <linux/ctype.h>
#include <linux/irq.h>
#include <linux/irqdesc.h> /*irq_to_desc*/
#include <linux/vmalloc.h>
#include <linux/preempt.h>
/* PD */
#include <tcpm.h>
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, &noti->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, &noti->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 <wy.chuang@mediatek.com>");
MODULE_DESCRIPTION("MTK PD Adapter Driver");
MODULE_LICENSE("GPL");