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)
881 lines
25 KiB
C
881 lines
25 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.
|
|
*/
|
|
#define pr_fmt(fmt) "[adapter_class] %s: " fmt, __func__
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/stat.h>
|
|
#include <linux/init.h>
|
|
#include <linux/ctype.h>
|
|
#include <linux/err.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/err.h> /* IS_ERR, PTR_ERR */
|
|
#include <linux/irq.h>
|
|
#include <linux/irqdesc.h> /*irq_to_desc*/
|
|
#include <linux/vmalloc.h>
|
|
#include <linux/preempt.h>
|
|
|
|
#include "adapter_class.h"
|
|
#include "tcpc_mi.h"
|
|
|
|
static struct class *adapter_class;
|
|
static const char * const usbpd_state_strings[] = {
|
|
"UNKNOWN",
|
|
/******************* Source *******************/
|
|
#ifdef CONFIG_USB_PD_PE_SOURCE
|
|
"SRC_STARTUP",
|
|
"SRC_DISCOVERY",
|
|
"SRC_SEND_CAPABILITIES",
|
|
"SRC_NEGOTIATE_CAPABILITIES",
|
|
"SRC_TRANSITION_SUPPLY",
|
|
"SRC_TRANSITION_SUPPLY2",
|
|
"SRC_Ready",
|
|
"SRC_DISABLED",
|
|
"SRC_CAPABILITY_RESPONSE",
|
|
"SRC_HARD_RESET",
|
|
"SRC_HARD_RESET_RECEIVED",
|
|
"SRC_TRANSITION_TO_DEFAULT",
|
|
"SRC_GET_SINK_CAP",
|
|
"SRC_WAIT_NEW_CAPABILITIES",
|
|
"SRC_SEND_SOFT_RESET",
|
|
"SRC_SOFT_RESET",
|
|
/* Source Startup Discover Cable */
|
|
#ifdef CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID
|
|
#ifdef CONFIG_PD_SRC_RESET_CABLE
|
|
"SRC_CBL_SEND_SOFT_RESET",
|
|
#endif /* CONFIG_PD_SRC_RESET_CABLE */
|
|
"SRC_VDM_IDENTITY_REQUEST",
|
|
"SRC_VDM_IDENTITY_ACKED",
|
|
"SRC_VDM_IDENTITY_NAKED",
|
|
#endif /* PD_CAP_PE_SRC_STARTUP_DISCOVER_ID */
|
|
/* Source for PD30 */
|
|
#ifdef CONFIG_USB_PD_REV30
|
|
"SRC_SEND_NOT_SUPPORTED",
|
|
"SRC_NOT_SUPPORTED_RECEIVED",
|
|
"SRC_CHUNK_RECEIVED",
|
|
#ifdef CONFIG_USB_PD_REV30_ALERT_LOCAL
|
|
"SRC_SEND_SOURCE_ALERT",
|
|
#endif /* CONFIG_USB_PD_REV30_ALERT_REMOTE */
|
|
#ifdef CONFIG_USB_PD_REV30_ALERT_REMOTE
|
|
"SRC_SINK_ALERT_RECEIVED",
|
|
#endif /* CONFIG_USB_PD_REV30_ALERT_REMOTE */
|
|
#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL
|
|
"SRC_GIVE_SOURCE_CAP_EXT",
|
|
#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL */
|
|
#ifdef CONFIG_USB_PD_REV30_STATUS_LOCAL
|
|
"SRC_GIVE_SOURCE_STATUS",
|
|
#endif /* CONFIG_USB_PD_REV30_STATUS_LOCAL */
|
|
#ifdef CONFIG_USB_PD_REV30_STATUS_REMOTE
|
|
"SRC_GET_SINK_STATUS",
|
|
#endif /* CONFIG_USB_PD_REV30_STATUS_REMOTE */
|
|
#ifdef CONFIG_USB_PD_REV30_PPS_SOURCE
|
|
"SRC_GIVE_PPS_STATUS",
|
|
#endif /* CONFIG_USB_PD_REV30_PPS_SOURCE */
|
|
#endif /* CONFIG_USB_PD_REV30 */
|
|
#endif /* CONFIG_USB_PD_PE_SOURCE */
|
|
/******************* Sink *******************/
|
|
#ifdef CONFIG_USB_PD_PE_SINK
|
|
/* Sink Init */
|
|
"SNK_STARTUP",
|
|
"SNK_DISCOVERY",
|
|
"SNK_WAIT_FOR_CAPABILITIES",
|
|
"SNK_EVALUATE_CAPABILITY",
|
|
"SNK_SELECT_CAPABILITY",
|
|
"SNK_TRANSITION_SINK",
|
|
"SNK_Ready",
|
|
"SNK_HARD_RESET",
|
|
"SNK_TRANSITION_TO_DEFAULT",
|
|
"SNK_GIVE_SINK_CAP",
|
|
"SNK_GET_SOURCE_CAP",
|
|
|
|
"SNK_SEND_SOFT_RESET",
|
|
"SNK_SOFT_RESET",
|
|
/* Sink for PD30 */
|
|
#ifdef CONFIG_USB_PD_REV30
|
|
"SNK_SEND_NOT_SUPPORTED",
|
|
"SNK_NOT_SUPPORTED_RECEIVED",
|
|
"SNK_CHUNK_RECEIVED",
|
|
#ifdef CONFIG_USB_PD_REV30_ALERT_REMOTE
|
|
"SNK_SOURCE_ALERT_RECEIVED",
|
|
#endif /* CONFIG_USB_PD_REV30_ALERT_REMOTE */
|
|
#ifdef CONFIG_USB_PD_REV30_ALERT_LOCAL
|
|
"SNK_SEND_SINK_ALERT",
|
|
#endif /* CONFIG_USB_PD_REV30_ALERT_LOCAL */
|
|
#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE
|
|
"SNK_GET_SOURCE_CAP_EXT",
|
|
#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE */
|
|
#ifdef CONFIG_USB_PD_REV30_STATUS_REMOTE
|
|
"SNK_GET_SOURCE_STATUS",
|
|
#endif /* CONFIG_USB_PD_REV30_STATUS_REMOTE */
|
|
#ifdef CONFIG_USB_PD_REV30_STATUS_LOCAL
|
|
"SNK_GIVE_SINK_STATUS",
|
|
#endif /* CONFIG_USB_PD_REV30_STATUS_LOCAL */
|
|
#ifdef CONFIG_USB_PD_REV30_PPS_SINK
|
|
"SNK_GET_PPS_STATUS",
|
|
#endif /* CONFIG_USB_PD_REV30_PPS_SINK */
|
|
#endif /* CONFIG_USB_PD_REV30 */
|
|
#endif /* CONFIG_USB_PD_PE_SINK */
|
|
/******************* DR_SWAP *******************/
|
|
#ifdef CONFIG_USB_PD_DR_SWAP
|
|
/* DR_SWAP_DFP */
|
|
"DRS_DFP_UFP_EVALUATE_DR_SWAP",
|
|
"DRS_DFP_UFP_ACCEPT_DR_SWAP",
|
|
"DRS_DFP_UFP_CHANGE_TO_UFP",
|
|
"DRS_DFP_UFP_SEND_DR_SWAP",
|
|
"DRS_DFP_UFP_REJECT_DR_SWAP",
|
|
/* DR_SWAP_UFP */
|
|
"DRS_UFP_DFP_EVALUATE_DR_SWAP",
|
|
"DRS_UFP_DFP_ACCEPT_DR_SWAP",
|
|
"DRS_UFP_DFP_CHANGE_TO_DFP",
|
|
"DRS_UFP_DFP_SEND_DR_SWAP",
|
|
"DRS_UFP_DFP_REJECT_DR_SWAP",
|
|
#endif /* CONFIG_USB_PD_DR_SWAP */
|
|
/******************* PR_SWAP *******************/
|
|
#ifdef CONFIG_USB_PD_PR_SWAP
|
|
/* PR_SWAP_SRC */
|
|
"PRS_SRC_SNK_EVALUATE_PR_SWAP",
|
|
"PRS_SRC_SNK_ACCEPT_PR_SWAP",
|
|
"PRS_SRC_SNK_TRANSITION_TO_OFF",
|
|
"PRS_SRC_SNK_ASSERT_RD",
|
|
"PRS_SRC_SNK_WAIT_SOURCE_ON",
|
|
"PRS_SRC_SNK_SEND_SWAP",
|
|
"PRS_SRC_SNK_REJECT_PR_SWAP",
|
|
/* PR_SWAP_SNK */
|
|
"PRS_SNK_SRC_EVALUATE_PR_SWAP",
|
|
"PRS_SNK_SRC_ACCEPT_PR_SWAP",
|
|
"PRS_SNK_SRC_TRANSITION_TO_OFF",
|
|
"PRS_SNK_SRC_ASSERT_RP",
|
|
"PRS_SNK_SRC_SOURCE_ON",
|
|
"PRS_SNK_SRC_SEND_SWAP",
|
|
"PRS_SNK_SRC_REJECT_SWAP",
|
|
/* get same role cap */
|
|
"DR_SRC_GET_SOURCE_CAP",
|
|
"DR_SRC_GIVE_SINK_CAP",
|
|
"DR_SNK_GET_SINK_CAP",
|
|
"DR_SNK_GIVE_SOURCE_CAP",
|
|
/* get same role cap for PD30 */
|
|
#ifdef CONFIG_USB_PD_REV30
|
|
#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL
|
|
"DR_SNK_GIVE_SOURCE_CAP_EXT",
|
|
#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL */
|
|
#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE
|
|
"DR_SRC_GET_SOURCE_CAP_EXT",
|
|
#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE */
|
|
#endif /* CONFIG_USB_PD_REV30 */
|
|
#endif /* CONFIG_USB_PD_PR_SWAP */
|
|
/******************* VCONN_SWAP *******************/
|
|
#ifdef CONFIG_USB_PD_VCONN_SWAP
|
|
"VCS_SEND_SWAP",
|
|
"VCS_EVALUATE_SWAP",
|
|
"VCS_ACCEPT_SWAP",
|
|
"VCS_REJECT_VCONN_SWAP",
|
|
"VCS_WAIT_FOR_VCONN",
|
|
"VCS_TURN_OFF_VCONN",
|
|
"VCS_TURN_ON_VCONN",
|
|
"VCS_SEND_PS_RDY",
|
|
#endif /* CONFIG_USB_PD_VCONN_SWAP */
|
|
/******************* UFP_VDM *******************/
|
|
"UFP_VDM_GET_IDENTITY",
|
|
"UFP_VDM_GET_SVIDS",
|
|
"UFP_VDM_GET_MODES",
|
|
"UFP_VDM_EVALUATE_MODE_ENTRY",
|
|
"UFP_VDM_MODE_EXIT",
|
|
"UFP_VDM_ATTENTION_REQUEST",
|
|
#ifdef CONFIG_USB_PD_ALT_MODE
|
|
"UFP_VDM_DP_STATUS_UPDATE",
|
|
"UFP_VDM_DP_CONFIGURE",
|
|
#endif/* CONFIG_USB_PD_ALT_MODE */
|
|
/******************* DFP_VDM *******************/
|
|
"DFP_UFP_VDM_IDENTITY_REQUEST",
|
|
"DFP_UFP_VDM_IDENTITY_ACKED",
|
|
"DFP_UFP_VDM_IDENTITY_NAKED",
|
|
"DFP_CBL_VDM_IDENTITY_REQUEST",
|
|
"DFP_CBL_VDM_IDENTITY_ACKED",
|
|
"DFP_CBL_VDM_IDENTITY_NAKED",
|
|
"DFP_VDM_SVIDS_REQUEST",
|
|
"DFP_VDM_SVIDS_ACKED",
|
|
"DFP_VDM_SVIDS_NAKED",
|
|
"DFP_VDM_MODES_REQUEST",
|
|
"DFP_VDM_MODES_ACKED",
|
|
"DFP_VDM_MODES_NAKED",
|
|
"DFP_VDM_MODE_ENTRY_REQUEST",
|
|
"DFP_VDM_MODE_ENTRY_ACKED",
|
|
"DFP_VDM_MODE_ENTRY_NAKED",
|
|
"DFP_VDM_MODE_EXIT_REQUEST",
|
|
"DFP_VDM_MODE_EXIT_ACKED",
|
|
"DFP_VDM_ATTENTION_REQUEST",
|
|
#ifdef CONFIG_PD_DFP_RESET_CABLE
|
|
"DFP_CBL_SEND_SOFT_RESET",
|
|
"DFP_CBL_SEND_CABLE_RESET",
|
|
#endif /* CONFIG_PD_DFP_RESET_CABLE */
|
|
#ifdef CONFIG_USB_PD_ALT_MODE_DFP
|
|
"DFP_VDM_DP_STATUS_UPDATE_REQUEST",
|
|
"DFP_VDM_DP_STATUS_UPDATE_ACKED",
|
|
"DFP_VDM_DP_STATUS_UPDATE_NAKED",
|
|
"DFP_VDM_DP_CONFIGURATION_REQUEST",
|
|
"DFP_VDM_DP_CONFIGURATION_ACKED",
|
|
"DFP_VDM_DP_CONFIGURATION_NAKED",
|
|
#endif/* CONFIG_USB_PD_ALT_MODE_DFP */
|
|
/******************* UVDM & SVDM *******************/
|
|
#ifdef CONFIG_USB_PD_CUSTOM_VDM
|
|
"UFP_UVDM_RECV",
|
|
"DFP_UVDM_SEND",
|
|
"DFP_UVDM_ACKED",
|
|
"DFP_UVDM_NAKED",
|
|
#endif/* CONFIG_USB_PD_CUSTOM_VDM */
|
|
/******************* PD30 Common *******************/
|
|
#ifdef CONFIG_USB_PD_REV30
|
|
#ifdef CONFIG_USB_PD_REV30_BAT_CAP_REMOTE
|
|
"GET_BATTERY_CAP",
|
|
#endif /* CONFIG_USB_PD_REV30_BAT_CAP_REMOTE */
|
|
#ifdef CONFIG_USB_PD_REV30_BAT_CAP_LOCAL
|
|
"GIVE_BATTERY_CAP",
|
|
#endif /* CONFIG_USB_PD_REV30_BAT_CAP_LOCAL */
|
|
#ifdef CONFIG_USB_PD_REV30_BAT_STATUS_REMOTE
|
|
"GET_BATTERY_STATUS",
|
|
#endif /* CONFIG_USB_PD_REV30_BAT_STATUS_REMOTE */
|
|
#ifdef CONFIG_USB_PD_REV30_BAT_STATUS_LOCAL
|
|
"GIVE_BATTERY_STATUS",
|
|
#endif /* CONFIG_USB_PD_REV30_BAT_STATUS_LOCAL */
|
|
#ifdef CONFIG_USB_PD_REV30_MFRS_INFO_REMOTE
|
|
"GET_MANUFACTURER_INFO",
|
|
#endif /* CONFIG_USB_PD_REV30_MFRS_INFO_REMOTE */
|
|
#ifdef CONFIG_USB_PD_REV30_MFRS_INFO_LOCAL
|
|
"GIVE_MANUFACTURER_INFO",
|
|
#endif /* CONFIG_USB_PD_REV30_MFRS_INFO_LOCAL */
|
|
#ifdef CONFIG_USB_PD_REV30_COUNTRY_CODE_REMOTE
|
|
"GET_COUNTRY_CODES",
|
|
#endif /* CONFIG_USB_PD_REV30_COUNTRY_CODE_REMOTE */
|
|
#ifdef CONFIG_USB_PD_REV30_COUNTRY_CODE_LOCAL
|
|
"GIVE_COUNTRY_CODES",
|
|
#endif /* CONFIG_USB_PD_REV30_COUNTRY_CODE_LOCAL */
|
|
#ifdef CONFIG_USB_PD_REV30_COUNTRY_INFO_REMOTE
|
|
"GET_COUNTRY_INFO",
|
|
#endif /* CONFIG_USB_PD_REV30_COUNTRY_INFO_REMOTE */
|
|
#ifdef CONFIG_USB_PD_REV30_COUNTRY_INFO_LOCAL
|
|
"GIVE_COUNTRY_INFO",
|
|
#endif /* CONFIG_USB_PD_REV30_COUNTRY_INFO_LOCAL */
|
|
"VDM_NOT_SUPPORTED",
|
|
#endif /* CONFIG_USB_PD_REV30 */
|
|
/******************* Others *******************/
|
|
#ifdef CONFIG_USB_PD_CUSTOM_DBGACC
|
|
"DBG_READY",
|
|
#endif/* CONFIG_USB_PD_CUSTOM_DBGACC */
|
|
#ifdef CONFIG_USB_PD_RECV_HRESET_COUNTER
|
|
"OVER_RECV_HRESET_LIMIT",
|
|
#endif/* CONFIG_USB_PD_RECV_HRESET_COUNTER */
|
|
"REJECT",
|
|
"ERROR_RECOVERY",
|
|
#ifdef CONFIG_USB_PD_ERROR_RECOVERY_ONCE
|
|
"ERROR_RECOVERY_ONCE",
|
|
#endif /* CONFIG_USB_PD_ERROR_RECOVERY_ONCE */
|
|
"BIST_TEST_DATA",
|
|
"BIST_CARRIER_MODE_2",
|
|
/* Wait tx finished */
|
|
"IDLE1",
|
|
"IDLE2",
|
|
"PD_NR_PE_STATES",
|
|
};
|
|
|
|
static ssize_t adapter_show_name(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct adapter_device *adapter_dev = to_adapter_device(dev);
|
|
|
|
return snprintf(buf, 20, "%s\n",
|
|
adapter_dev->props.alias_name ?
|
|
adapter_dev->props.alias_name : "anonymous");
|
|
}
|
|
|
|
static int adapter_suspend(struct device *dev, pm_message_t state)
|
|
{
|
|
struct adapter_device *adapter_dev = to_adapter_device(dev);
|
|
|
|
if (adapter_dev->ops->suspend)
|
|
return adapter_dev->ops->suspend(adapter_dev, state);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int adapter_resume(struct device *dev)
|
|
{
|
|
struct adapter_device *adapter_dev = to_adapter_device(dev);
|
|
|
|
if (adapter_dev->ops->resume)
|
|
return adapter_dev->ops->resume(adapter_dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void adapter_device_release(struct device *dev)
|
|
{
|
|
struct adapter_device *adapter_dev = to_adapter_device(dev);
|
|
|
|
kfree(adapter_dev);
|
|
}
|
|
|
|
int adapter_dev_get_property(struct adapter_device *adapter_dev,
|
|
enum adapter_property sta)
|
|
{
|
|
if (adapter_dev != NULL && adapter_dev->ops != NULL &&
|
|
adapter_dev->ops->get_property)
|
|
return adapter_dev->ops->get_property(adapter_dev, sta);
|
|
|
|
return -ENOTSUPP;
|
|
}
|
|
EXPORT_SYMBOL(adapter_dev_get_property);
|
|
|
|
int adapter_dev_get_status(struct adapter_device *adapter_dev,
|
|
struct adapter_status *sta)
|
|
{
|
|
if (adapter_dev != NULL && adapter_dev->ops != NULL &&
|
|
adapter_dev->ops->get_status)
|
|
return adapter_dev->ops->get_status(adapter_dev, sta);
|
|
|
|
return -ENOTSUPP;
|
|
}
|
|
EXPORT_SYMBOL(adapter_dev_get_status);
|
|
|
|
int adapter_dev_get_output(struct adapter_device *adapter_dev, int *mV, int *mA)
|
|
{
|
|
if (adapter_dev != NULL && adapter_dev->ops != NULL &&
|
|
adapter_dev->ops->get_output)
|
|
return adapter_dev->ops->get_output(adapter_dev, mV, mA);
|
|
|
|
return -ENOTSUPP;
|
|
}
|
|
EXPORT_SYMBOL(adapter_dev_get_output);
|
|
|
|
int adapter_dev_set_cap(struct adapter_device *adapter_dev,
|
|
enum adapter_cap_type type,
|
|
int mV, int mA)
|
|
{
|
|
if (adapter_dev != NULL && adapter_dev->ops != NULL &&
|
|
adapter_dev->ops->set_cap)
|
|
return adapter_dev->ops->set_cap(adapter_dev, type, mV, mA);
|
|
|
|
return -ENOTSUPP;
|
|
}
|
|
EXPORT_SYMBOL(adapter_dev_set_cap);
|
|
|
|
int adapter_dev_set_cap_bq(struct adapter_device *adapter_dev,
|
|
enum adapter_cap_type type,
|
|
int mV, int mA)
|
|
{
|
|
if (adapter_dev != NULL && adapter_dev->ops != NULL &&
|
|
adapter_dev->ops->set_cap_bq)
|
|
return adapter_dev->ops->set_cap_bq(adapter_dev, type, mV, mA);
|
|
|
|
return -ENOTSUPP;
|
|
}
|
|
EXPORT_SYMBOL(adapter_dev_set_cap_bq);
|
|
|
|
int adapter_dev_get_cap(struct adapter_device *adapter_dev,
|
|
enum adapter_cap_type type,
|
|
struct adapter_power_cap *cap)
|
|
{
|
|
if (adapter_dev != NULL && adapter_dev->ops != NULL &&
|
|
adapter_dev->ops->get_cap)
|
|
return adapter_dev->ops->get_cap(adapter_dev, type, cap);
|
|
|
|
return -ENOTSUPP;
|
|
}
|
|
EXPORT_SYMBOL(adapter_dev_get_cap);
|
|
|
|
int adapter_dev_get_id(struct adapter_device *adapter_dev)
|
|
{
|
|
if (adapter_dev != NULL && adapter_dev->ops != NULL &&
|
|
adapter_dev->ops->get_svid)
|
|
return adapter_dev->ops->get_svid(adapter_dev);
|
|
|
|
return -ENOTSUPP;
|
|
}
|
|
EXPORT_SYMBOL(adapter_dev_get_id);
|
|
|
|
int adapter_dev_get_svid(struct adapter_device *adapter_dev)
|
|
{
|
|
if (adapter_dev != NULL && adapter_dev->ops != NULL &&
|
|
adapter_dev->ops->get_svid)
|
|
return adapter_dev->ops->get_svid(adapter_dev);
|
|
|
|
return -ENOTSUPP;
|
|
}
|
|
EXPORT_SYMBOL(adapter_dev_get_svid);
|
|
|
|
int adapter_dev_request_vdm_cmd(struct adapter_device *adapter_dev,
|
|
enum uvdm_state cmd,
|
|
unsigned char *data,
|
|
unsigned int data_len)
|
|
{
|
|
if (adapter_dev != NULL && adapter_dev->ops != NULL &&
|
|
adapter_dev->ops->request_vdm_cmd)
|
|
return adapter_dev->ops->request_vdm_cmd(adapter_dev, cmd, data, data_len);
|
|
|
|
return -ENOTSUPP;
|
|
}
|
|
EXPORT_SYMBOL(adapter_dev_request_vdm_cmd);
|
|
|
|
|
|
static ssize_t adapter_id_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct adapter_device *adapter_dev = to_adapter_device(dev);
|
|
|
|
if (adapter_dev != NULL && adapter_dev->ops != NULL &&
|
|
adapter_dev->ops->get_svid) {
|
|
adapter_dev->ops->get_svid(adapter_dev);
|
|
}
|
|
pr_info("%s: adapter_id is %08x\n", __func__, adapter_dev->adapter_id);
|
|
|
|
return snprintf(buf, PAGE_SIZE, "%08x\n", adapter_dev->adapter_id);
|
|
}
|
|
static DEVICE_ATTR_RO(adapter_id);
|
|
|
|
static ssize_t adapter_svid_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct adapter_device *adapter_dev = to_adapter_device(dev);
|
|
|
|
if (adapter_dev != NULL && adapter_dev->ops != NULL &&
|
|
adapter_dev->ops->get_svid) {
|
|
adapter_dev->ops->get_svid(adapter_dev);
|
|
}
|
|
pr_info("%s: adapter_svid is %04x\n", __func__, adapter_dev->adapter_svid);
|
|
|
|
return snprintf(buf, PAGE_SIZE, "%04x\n", adapter_dev->adapter_svid);
|
|
}
|
|
static DEVICE_ATTR_RO(adapter_svid);
|
|
|
|
static int StringToHex(char *str, unsigned char *out, unsigned int *outlen)
|
|
{
|
|
char *p = str;
|
|
char high = 0, low = 0;
|
|
int tmplen = strlen(p), cnt = 0;
|
|
tmplen = strlen(p);
|
|
while (cnt < (tmplen / 2)) {
|
|
high = ((*p > '9') && ((*p <= 'F') || (*p <= 'f'))) ? *p - 48 - 7 : *p - 48;
|
|
low = (*(++p) > '9' && ((*p <= 'F') || (*p <= 'f'))) ? *(p) - 48 - 7 : *(p) - 48;
|
|
out[cnt] = ((high & 0x0f) << 4 | (low & 0x0f));
|
|
p++;
|
|
cnt++;
|
|
}
|
|
if (tmplen % 2 != 0)
|
|
out[cnt] = ((*p > '9') && ((*p <= 'F') || (*p <= 'f'))) ? *p - 48 - 7 : *p - 48;
|
|
|
|
if (outlen != NULL)
|
|
*outlen = tmplen / 2 + tmplen % 2;
|
|
|
|
return tmplen / 2 + tmplen % 2;
|
|
}
|
|
|
|
static ssize_t request_vdm_cmd_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t size)
|
|
{
|
|
struct adapter_device *adapter_dev = to_adapter_device(dev);
|
|
int cmd, ret;
|
|
unsigned char buffer[64];
|
|
unsigned char *data;
|
|
unsigned int count;
|
|
int i;
|
|
|
|
if (in_interrupt()) {
|
|
data = kmalloc(40, GFP_ATOMIC);
|
|
pr_info("%s: kmalloc atomic ok.\n", __func__);
|
|
} else {
|
|
data = kmalloc(40, GFP_KERNEL);
|
|
pr_info("%s: kmalloc kernel ok.\n", __func__);
|
|
}
|
|
memset(data, 0, 40);
|
|
|
|
ret = sscanf(buf, "%d,%s\n", &cmd, buffer);
|
|
pr_info("%s:cmd:%d, buffer:%s\n", __func__, cmd, buffer);
|
|
|
|
StringToHex(buffer, data, &count);
|
|
pr_info("%s:count = %d\n", __func__, count);
|
|
|
|
for (i = 0; i < count; i++)
|
|
pr_info("%02x", data[i]);
|
|
|
|
if (adapter_dev != NULL && adapter_dev->ops != NULL &&
|
|
adapter_dev->ops->request_vdm_cmd) {
|
|
adapter_dev->ops->request_vdm_cmd(adapter_dev,
|
|
cmd, data, count);
|
|
}
|
|
kfree(data);
|
|
|
|
return size;
|
|
}
|
|
|
|
static ssize_t request_vdm_cmd_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct adapter_device *adapter_dev = to_adapter_device(dev);
|
|
int i;
|
|
char data[16], str_buf[128] = {0};
|
|
int cmd = adapter_dev->uvdm_state;
|
|
|
|
switch (cmd) {
|
|
case USBPD_UVDM_CHARGER_VERSION:
|
|
return snprintf(buf, PAGE_SIZE, "%d,%x\n",
|
|
cmd, adapter_dev->vdm_data.ta_version);
|
|
case USBPD_UVDM_CHARGER_TEMP:
|
|
return snprintf(buf, PAGE_SIZE, "%d,%d\n",
|
|
cmd, adapter_dev->vdm_data.ta_temp);
|
|
case USBPD_UVDM_CHARGER_VOLTAGE:
|
|
return snprintf(buf, PAGE_SIZE, "%d,%d\n",
|
|
cmd, adapter_dev->vdm_data.ta_voltage);
|
|
case USBPD_UVDM_SESSION_SEED:
|
|
case USBPD_UVDM_CONNECT:
|
|
case USBPD_UVDM_DISCONNECT:
|
|
case USBPD_UVDM_VERIFIED:
|
|
case USBPD_UVDM_REMOVE_COMPENSATION:
|
|
case USBPD_UVDM_NAN_ACK:
|
|
return snprintf(buf, PAGE_SIZE, "%d,Null\n", cmd);
|
|
case USBPD_UVDM_AUTHENTICATION:
|
|
for (i = 0; i < USBPD_UVDM_SS_LEN; i++) {
|
|
memset(data, 0, sizeof(data));
|
|
snprintf(data, sizeof(data), "%08lx",
|
|
adapter_dev->vdm_data.digest[i]);
|
|
strlcat(str_buf, data, sizeof(str_buf));
|
|
}
|
|
return snprintf(buf, PAGE_SIZE, "%d,%s\n", cmd, str_buf);
|
|
case USBPD_UVDM_REVERSE_AUTHEN:
|
|
return snprintf(buf, PAGE_SIZE, "%d,%d", cmd, adapter_dev->vdm_data.reauth);
|
|
default:
|
|
pr_err("feedbak cmd:%d is not support\n", cmd);
|
|
break;
|
|
}
|
|
return snprintf(buf, PAGE_SIZE, "%d,%s\n", cmd, str_buf);
|
|
|
|
}
|
|
static DEVICE_ATTR_RW(request_vdm_cmd);
|
|
|
|
static ssize_t verify_process_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t size)
|
|
{
|
|
struct adapter_device *adapter_dev = to_adapter_device(dev);
|
|
int val;
|
|
|
|
if (sscanf(buf, "%d\n", &val) != 1) {
|
|
adapter_dev->verify_process = 0;
|
|
return -EINVAL;
|
|
}
|
|
|
|
adapter_dev->verify_process = !!val;
|
|
|
|
pr_info("%s: batterysecret verify process :%d\n",
|
|
__func__, adapter_dev->verify_process);
|
|
|
|
if (adapter_dev != NULL && adapter_dev->ops != NULL &&
|
|
adapter_dev->ops->set_pd_verify_process) {
|
|
adapter_dev->ops->set_pd_verify_process(adapter_dev,
|
|
adapter_dev->verify_process);
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
static ssize_t verify_process_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct adapter_device *adapter_dev = to_adapter_device(dev);
|
|
|
|
return snprintf(buf, PAGE_SIZE, "%d\n", adapter_dev->verify_process);
|
|
}
|
|
static DEVICE_ATTR_RW(verify_process);
|
|
|
|
static ssize_t usbpd_verifed_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t size)
|
|
{
|
|
struct adapter_device *adapter_dev = to_adapter_device(dev);
|
|
int val = 0;
|
|
struct adapter_power_cap cap = {0};
|
|
|
|
if (sscanf(buf, "%d\n", &val) != 1) {
|
|
adapter_dev->verifed = 0;
|
|
return -EINVAL;
|
|
}
|
|
pr_info("%s: batteryd set usbpd verifyed :%d\n", __func__, val);
|
|
adapter_dev->verifed = !!val;
|
|
|
|
if (adapter_dev->verifed) {
|
|
if (adapter_dev != NULL && adapter_dev->ops != NULL &&
|
|
adapter_dev->ops->get_cap)
|
|
adapter_dev->ops->get_cap(adapter_dev, MTK_PD_APDO_REGAIN, &cap);
|
|
}
|
|
return size;
|
|
}
|
|
|
|
static ssize_t usbpd_verifed_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct adapter_device *adapter_dev = to_adapter_device(dev);
|
|
|
|
return snprintf(buf, PAGE_SIZE, "%d\n", adapter_dev->verifed);
|
|
}
|
|
static DEVICE_ATTR_RW(usbpd_verifed);
|
|
|
|
static ssize_t current_pr_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct adapter_device *adapter_dev = to_adapter_device(dev);
|
|
const char *pr = "none";
|
|
|
|
if (adapter_dev != NULL && adapter_dev->ops != NULL &&
|
|
adapter_dev->ops->get_power_role) {
|
|
adapter_dev->ops->get_power_role(adapter_dev);
|
|
}
|
|
pr_info("%s: current_pr is %d\n", __func__, adapter_dev->role);
|
|
if (adapter_dev->role == PD_ROLE_SINK_FOR_ADAPTER)
|
|
pr = "sink";
|
|
else if (adapter_dev->role == PD_ROLE_SOURCE_FOR_ADAPTER)
|
|
pr = "source";
|
|
|
|
return snprintf(buf, PAGE_SIZE, "%s\n", pr);
|
|
}
|
|
static DEVICE_ATTR_RO(current_pr);
|
|
|
|
static ssize_t current_state_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct adapter_device *adapter_dev = to_adapter_device(dev);
|
|
|
|
if (adapter_dev != NULL && adapter_dev->ops != NULL &&
|
|
adapter_dev->ops->get_current_state) {
|
|
adapter_dev->ops->get_current_state(adapter_dev);
|
|
}
|
|
pr_err("%s: current_state is %d\n", __func__, adapter_dev->current_state);
|
|
|
|
if (adapter_dev->current_state >= (sizeof(usbpd_state_strings) / sizeof(usbpd_state_strings[0])))
|
|
adapter_dev->current_state = 0;
|
|
|
|
pr_err("%s: %s\n", __func__,
|
|
usbpd_state_strings[adapter_dev->current_state]);
|
|
|
|
return snprintf(buf, PAGE_SIZE, "%s\n",
|
|
usbpd_state_strings[adapter_dev->current_state]);
|
|
}
|
|
static DEVICE_ATTR_RO(current_state);
|
|
|
|
static ssize_t pdo_n_show(struct device *dev, struct device_attribute *attr,
|
|
char *buf);
|
|
|
|
#define PDO_ATTR(n) { \
|
|
.attr = { .name = __stringify(pdo##n), .mode = 0444 }, \
|
|
.show = pdo_n_show, \
|
|
}
|
|
|
|
static struct device_attribute dev_attr_pdos[] = {
|
|
PDO_ATTR(1),
|
|
PDO_ATTR(2),
|
|
PDO_ATTR(3),
|
|
PDO_ATTR(4),
|
|
PDO_ATTR(5),
|
|
PDO_ATTR(6),
|
|
PDO_ATTR(7),
|
|
};
|
|
static ssize_t pdo_n_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct adapter_device *adapter_dev = to_adapter_device(dev);
|
|
int i;
|
|
|
|
if (adapter_dev != NULL && adapter_dev->ops != NULL &&
|
|
adapter_dev->ops->get_pdos) {
|
|
adapter_dev->ops->get_pdos(adapter_dev);
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_SIZE(dev_attr_pdos); i++) {
|
|
if (attr == &dev_attr_pdos[i])
|
|
/* dump the PDO as a hex string */
|
|
return snprintf(buf, PAGE_SIZE, "%08x\n",
|
|
adapter_dev->received_pdos[i]);
|
|
}
|
|
|
|
pr_err("%s: Invalid PDO index\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
static DEVICE_ATTR(name, 0444, adapter_show_name, NULL);
|
|
|
|
static struct attribute *adapter_class_attrs[] = {
|
|
&dev_attr_name.attr,
|
|
&dev_attr_request_vdm_cmd.attr,
|
|
&dev_attr_current_state.attr,
|
|
&dev_attr_adapter_id.attr,
|
|
&dev_attr_adapter_svid.attr,
|
|
&dev_attr_verify_process.attr,
|
|
&dev_attr_usbpd_verifed.attr,
|
|
&dev_attr_current_pr.attr,
|
|
&dev_attr_pdos[0].attr,
|
|
&dev_attr_pdos[1].attr,
|
|
&dev_attr_pdos[2].attr,
|
|
&dev_attr_pdos[3].attr,
|
|
&dev_attr_pdos[4].attr,
|
|
&dev_attr_pdos[5].attr,
|
|
&dev_attr_pdos[6].attr,
|
|
NULL,
|
|
};
|
|
|
|
static const struct attribute_group adapter_group = {
|
|
.attrs = adapter_class_attrs,
|
|
};
|
|
|
|
static const struct attribute_group *adapter_groups[] = {
|
|
&adapter_group,
|
|
NULL,
|
|
};
|
|
|
|
int register_adapter_device_notifier(struct adapter_device *adapter_dev,
|
|
struct notifier_block *nb)
|
|
{
|
|
int ret;
|
|
|
|
ret = srcu_notifier_chain_register(&adapter_dev->evt_nh, nb);
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL(register_adapter_device_notifier);
|
|
|
|
int unregister_adapter_device_notifier(struct adapter_device *adapter_dev,
|
|
struct notifier_block *nb)
|
|
{
|
|
return srcu_notifier_chain_unregister(&adapter_dev->evt_nh, nb);
|
|
}
|
|
EXPORT_SYMBOL(unregister_adapter_device_notifier);
|
|
|
|
/**
|
|
* adapter_device_register - create and register a new object of
|
|
* adapter_device class.
|
|
* @name: the name of the new object
|
|
* @parent: a pointer to the parent device
|
|
* @devdata: an optional pointer to be stored for private driver use.
|
|
* The methods may retrieve it by using adapter_get_data(adapter_dev).
|
|
* @ops: the charger operations structure.
|
|
*
|
|
* Creates and registers new charger device. Returns either an
|
|
* ERR_PTR() or a pointer to the newly allocated device.
|
|
*/
|
|
struct adapter_device *adapter_device_register(const char *name,
|
|
struct device *parent, void *devdata,
|
|
const struct adapter_ops *ops,
|
|
const struct adapter_properties *props)
|
|
{
|
|
struct adapter_device *adapter_dev = NULL;
|
|
static struct lock_class_key key;
|
|
struct srcu_notifier_head *head = NULL;
|
|
int rc;
|
|
char *adapter_name = NULL;
|
|
|
|
pr_notice("%s: name=%s\n", __func__, name);
|
|
adapter_dev = kzalloc(sizeof(*adapter_dev), GFP_KERNEL);
|
|
if (!adapter_dev)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
head = &adapter_dev->evt_nh;
|
|
srcu_init_notifier_head(head);
|
|
/* Rename srcu's lock to avoid LockProve warning */
|
|
lockdep_init_map(&(&head->srcu)->dep_map, name, &key, 0);
|
|
mutex_init(&adapter_dev->ops_lock);
|
|
adapter_dev->dev.class = adapter_class;
|
|
adapter_dev->dev.parent = parent;
|
|
adapter_dev->dev.release = adapter_device_release;
|
|
adapter_name = kasprintf(GFP_KERNEL, "%s", name);
|
|
dev_set_name(&adapter_dev->dev, adapter_name);
|
|
dev_set_drvdata(&adapter_dev->dev, devdata);
|
|
kfree(adapter_name);
|
|
|
|
/* Copy properties */
|
|
if (props) {
|
|
memcpy(&adapter_dev->props, props,
|
|
sizeof(struct adapter_properties));
|
|
}
|
|
rc = device_register(&adapter_dev->dev);
|
|
if (rc) {
|
|
kfree(adapter_dev);
|
|
return ERR_PTR(rc);
|
|
}
|
|
adapter_dev->ops = ops;
|
|
return adapter_dev;
|
|
}
|
|
EXPORT_SYMBOL(adapter_device_register);
|
|
|
|
/**
|
|
* adapter_device_unregister - unregisters a switching charger device
|
|
* object.
|
|
* @adapter_dev: the switching charger device object to be unregistered
|
|
* and freed.
|
|
*
|
|
* Unregisters a previously registered via adapter_device_register object.
|
|
*/
|
|
void adapter_device_unregister(struct adapter_device *adapter_dev)
|
|
{
|
|
if (!adapter_dev)
|
|
return;
|
|
|
|
mutex_lock(&adapter_dev->ops_lock);
|
|
adapter_dev->ops = NULL;
|
|
mutex_unlock(&adapter_dev->ops_lock);
|
|
device_unregister(&adapter_dev->dev);
|
|
}
|
|
EXPORT_SYMBOL(adapter_device_unregister);
|
|
|
|
|
|
static int adapter_match_device_by_name(struct device *dev,
|
|
const void *data)
|
|
{
|
|
const char *name = data;
|
|
|
|
return strcmp(dev_name(dev), name) == 0;
|
|
}
|
|
|
|
struct adapter_device *get_adapter_by_name(const char *name)
|
|
{
|
|
struct device *dev = NULL;
|
|
|
|
if (!name)
|
|
return (struct adapter_device *)NULL;
|
|
dev = class_find_device(adapter_class, NULL, name,
|
|
adapter_match_device_by_name);
|
|
|
|
return dev ? to_adapter_device(dev) : NULL;
|
|
|
|
}
|
|
EXPORT_SYMBOL(get_adapter_by_name);
|
|
|
|
static void __exit adapter_class_exit(void)
|
|
{
|
|
class_destroy(adapter_class);
|
|
}
|
|
|
|
static int __init adapter_class_init(void)
|
|
{
|
|
adapter_class = class_create(THIS_MODULE, "Charging_Adapter");
|
|
if (IS_ERR(adapter_class)) {
|
|
pr_notice("Unable to create Charging Adapter class; errno = %ld\n",
|
|
PTR_ERR(adapter_class));
|
|
return PTR_ERR(adapter_class);
|
|
}
|
|
adapter_class->dev_groups = adapter_groups;
|
|
adapter_class->suspend = adapter_suspend;
|
|
adapter_class->resume = adapter_resume;
|
|
return 0;
|
|
}
|
|
|
|
subsys_initcall(adapter_class_init);
|
|
module_exit(adapter_class_exit);
|
|
|
|
MODULE_DESCRIPTION("Adapter Class Device");
|
|
MODULE_AUTHOR("Wy Chuang <wy.chuang@mediatek.com>");
|
|
MODULE_LICENSE("GPL");
|
|
|