1649 lines
47 KiB
C
1649 lines
47 KiB
C
/*
|
|
* USB PD Driver - Device Policy Manager
|
|
*/
|
|
|
|
#include <linux/device.h>
|
|
#include <linux/workqueue.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/ccic/usbpd.h>
|
|
|
|
#if defined(CONFIG_CCIC_S2MU004)
|
|
#include <linux/ccic/usbpd-s2mu004.h>
|
|
#endif
|
|
|
|
#if defined(CONFIG_CCIC_S2MU106)
|
|
#include <linux/ccic/usbpd-s2mu106.h>
|
|
#endif
|
|
|
|
#if defined(CONFIG_CCIC_S2MU205)
|
|
#include <linux/ccic/usbpd-s2mu205.h>
|
|
#endif
|
|
|
|
#include <linux/of_gpio.h>
|
|
|
|
#include <linux/muic/muic.h>
|
|
#if defined(CONFIG_MUIC_NOTIFIER)
|
|
#include <linux/muic/muic_notifier.h>
|
|
#endif /* CONFIG_MUIC_NOTIFIER */
|
|
#include <linux/ccic/pdic_notifier.h>
|
|
#ifdef CONFIG_BATTERY_SAMSUNG
|
|
#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER
|
|
#include <linux/battery/battery_notifier.h>
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(CONFIG_CCIC_NOTIFIER)
|
|
#include <linux/ccic/ccic_notifier.h>
|
|
#endif
|
|
|
|
#if (defined CONFIG_CCIC_NOTIFIER || defined CONFIG_DUAL_ROLE_USB_INTF)
|
|
#include <linux/ccic/usbpd_ext.h>
|
|
#endif
|
|
|
|
/* switch device header */
|
|
#if defined(CONFIG_SWITCH)
|
|
#include <linux/switch.h>
|
|
#endif /* CONFIG_SWITCH */
|
|
|
|
#ifdef CONFIG_USB_HOST_NOTIFY
|
|
#include <linux/usb_notify.h>
|
|
#endif
|
|
|
|
#include <linux/completion.h>
|
|
#include <linux/ccic/ccic_misc.h>
|
|
|
|
#if defined(CONFIG_SWITCH)
|
|
static struct switch_dev switch_dock = {
|
|
.name = "ccic_dock",
|
|
};
|
|
#endif
|
|
|
|
void usbpd_manager_select_pdo_cancel(struct device *dev);
|
|
|
|
#ifdef CONFIG_BATTERY_SAMSUNG
|
|
#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER
|
|
void select_pdo(int num);
|
|
void usbpd_manager_select_pdo(int num);
|
|
void (*fp_select_pdo)(int num);
|
|
|
|
#if defined(CONFIG_CCIC_NOTIFIER)
|
|
extern struct device *ccic_device;
|
|
#endif
|
|
|
|
void usbpd_manager_select_pdo(int num)
|
|
{
|
|
struct usbpd_data *pd_data = pd_noti.pd_data;
|
|
struct usbpd_manager_data *manager = &pd_data->manager;
|
|
bool vbus_short;
|
|
|
|
pd_data->phy_ops.get_vbus_short_check(pd_data, &vbus_short);
|
|
|
|
if (vbus_short) {
|
|
pr_info(" %s : PDO(%d) is ignored becasue of vbus short\n",
|
|
__func__, pd_noti.sink_status.selected_pdo_num);
|
|
return;
|
|
}
|
|
|
|
mutex_lock(&manager->pdo_mutex);
|
|
if (manager->flash_mode == 1)
|
|
goto exit;
|
|
|
|
if (pd_noti.sink_status.selected_pdo_num == num)
|
|
goto exit;
|
|
else if (num > pd_noti.sink_status.available_pdo_num)
|
|
pd_noti.sink_status.selected_pdo_num = pd_noti.sink_status.available_pdo_num;
|
|
else if (num < 1)
|
|
pd_noti.sink_status.selected_pdo_num = 1;
|
|
else
|
|
pd_noti.sink_status.selected_pdo_num = num;
|
|
pr_info(" %s : PDO(%d) is selected to change\n", __func__, pd_noti.sink_status.selected_pdo_num);
|
|
|
|
schedule_delayed_work(&manager->select_pdo_handler, msecs_to_jiffies(50));
|
|
exit:
|
|
mutex_unlock(&manager->pdo_mutex);
|
|
}
|
|
|
|
void select_pdo(int num)
|
|
{
|
|
if (fp_select_pdo)
|
|
fp_select_pdo(num);
|
|
}
|
|
|
|
void pdo_ctrl_by_flash(bool mode)
|
|
{
|
|
struct usbpd_data *pd_data = pd_noti.pd_data;
|
|
struct usbpd_manager_data *manager = &pd_data->manager;
|
|
|
|
pr_info("%s: mode(%d)\n", __func__, mode);
|
|
|
|
mutex_lock(&manager->pdo_mutex);
|
|
if (mode)
|
|
manager->flash_mode = 1;
|
|
else
|
|
manager->flash_mode = 0;
|
|
|
|
if (pd_noti.sink_status.selected_pdo_num != 0) {
|
|
pd_noti.sink_status.selected_pdo_num = 1;
|
|
usbpd_manager_select_pdo_cancel(pd_data->dev);
|
|
usbpd_manager_inform_event(pd_noti.pd_data, MANAGER_GET_SRC_CAP);
|
|
}
|
|
mutex_unlock(&manager->pdo_mutex);
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
void usbpd_manager_select_pdo_handler(struct work_struct *work)
|
|
{
|
|
pr_info("%s: call select pdo handler\n", __func__);
|
|
|
|
usbpd_manager_inform_event(pd_noti.pd_data, MANAGER_NEW_POWER_SRC);
|
|
|
|
}
|
|
|
|
void usbpd_manager_select_pdo_cancel(struct device *dev)
|
|
{
|
|
struct usbpd_data *pd_data = dev_get_drvdata(dev);
|
|
struct usbpd_manager_data *manager = &pd_data->manager;
|
|
|
|
cancel_delayed_work_sync(&manager->select_pdo_handler);
|
|
}
|
|
|
|
void usbpd_manager_start_discover_msg_handler(struct work_struct *work)
|
|
{
|
|
struct usbpd_manager_data *manager =
|
|
container_of(work, struct usbpd_manager_data,
|
|
start_discover_msg_handler.work);
|
|
pr_info("%s: call handler\n", __func__);
|
|
|
|
mutex_lock(&manager->vdm_mutex);
|
|
if (manager->alt_sended == 0 && manager->vdm_en == 1) {
|
|
usbpd_manager_inform_event(pd_noti.pd_data,
|
|
MANAGER_START_DISCOVER_IDENTITY);
|
|
manager->alt_sended = 1;
|
|
}
|
|
mutex_unlock(&manager->vdm_mutex);
|
|
}
|
|
|
|
void usbpd_manager_start_discover_msg_cancel(struct device *dev)
|
|
{
|
|
struct usbpd_data *pd_data = dev_get_drvdata(dev);
|
|
struct usbpd_manager_data *manager = &pd_data->manager;
|
|
|
|
cancel_delayed_work_sync(&manager->start_discover_msg_handler);
|
|
}
|
|
|
|
void usbpd_manager_send_pr_swap(struct device *dev)
|
|
{
|
|
pr_info("%s: call send pr swap msg\n", __func__);
|
|
|
|
usbpd_manager_inform_event(pd_noti.pd_data, MANAGER_SEND_PR_SWAP);
|
|
}
|
|
|
|
void usbpd_manager_send_dr_swap(struct device *dev)
|
|
{
|
|
pr_info("%s: call send pr swap msg\n", __func__);
|
|
|
|
usbpd_manager_inform_event(pd_noti.pd_data, MANAGER_SEND_DR_SWAP);
|
|
}
|
|
|
|
static void init_source_cap_data(struct usbpd_manager_data *_data)
|
|
{
|
|
/* struct usbpd_data *pd_data = manager_to_usbpd(_data);
|
|
int val; */
|
|
msg_header_type *msg_header = &_data->pd_data->source_msg_header;
|
|
data_obj_type *data_obj = &_data->pd_data->source_data_obj;
|
|
|
|
msg_header->msg_type = USBPD_Source_Capabilities;
|
|
/* pd_data->phy_ops.get_power_role(pd_data, &val); */
|
|
msg_header->port_data_role = USBPD_DFP;
|
|
msg_header->port_power_role = USBPD_SOURCE;
|
|
msg_header->num_data_objs = 1;
|
|
|
|
data_obj->power_data_obj.max_current = 500 / 10;
|
|
data_obj->power_data_obj.voltage = 5000 / 50;
|
|
data_obj->power_data_obj.supply = POWER_TYPE_FIXED;
|
|
data_obj->power_data_obj.data_role_swap = 1;
|
|
data_obj->power_data_obj.dual_role_power = 1;
|
|
data_obj->power_data_obj.usb_suspend_support = 1;
|
|
data_obj->power_data_obj.usb_comm_capable = 1;
|
|
|
|
}
|
|
|
|
static void init_sink_cap_data(struct usbpd_manager_data *_data)
|
|
{
|
|
/* struct usbpd_data *pd_data = manager_to_usbpd(_data);
|
|
int val; */
|
|
msg_header_type *msg_header = &_data->pd_data->sink_msg_header;
|
|
data_obj_type *data_obj = _data->pd_data->sink_data_obj;
|
|
|
|
msg_header->msg_type = USBPD_Sink_Capabilities;
|
|
/* pd_data->phy_ops.get_power_role(pd_data, &val); */
|
|
msg_header->port_data_role = USBPD_UFP;
|
|
msg_header->port_power_role = USBPD_SINK;
|
|
msg_header->num_data_objs = 2;
|
|
|
|
data_obj->power_data_obj_sink.supply_type = POWER_TYPE_FIXED;
|
|
data_obj->power_data_obj_sink.dual_role_power = 1;
|
|
data_obj->power_data_obj_sink.higher_capability = 1;
|
|
data_obj->power_data_obj_sink.externally_powered = 0;
|
|
data_obj->power_data_obj_sink.usb_comm_capable = 1;
|
|
data_obj->power_data_obj_sink.data_role_swap = 1;
|
|
data_obj->power_data_obj_sink.voltage = 5000/50;
|
|
data_obj->power_data_obj_sink.op_current = 3000/10;
|
|
|
|
(data_obj + 1)->power_data_obj_sink.supply_type = POWER_TYPE_FIXED;
|
|
(data_obj + 1)->power_data_obj_sink.voltage = 9000/50;
|
|
(data_obj + 1)->power_data_obj_sink.op_current = 2000/10;
|
|
}
|
|
|
|
int samsung_uvdm_ready(void)
|
|
{
|
|
int uvdm_ready = false;
|
|
#if defined CONFIG_CCIC_S2MU004
|
|
struct s2mu004_usbpd_data *pdic_data;
|
|
#elif defined CONFIG_CCIC_S2MU106
|
|
struct s2mu106_usbpd_data *pdic_data;
|
|
#elif defined CONFIG_CCIC_S2MU205
|
|
struct s2mu205_usbpd_data *pdic_data;
|
|
#endif
|
|
struct usbpd_data *pd_data;
|
|
struct usbpd_manager_data *manager;
|
|
|
|
pr_info("%s\n", __func__);
|
|
if(!ccic_device) {
|
|
pr_info("ccic_dev is null\n");
|
|
return -ENXIO;
|
|
}
|
|
|
|
pdic_data = dev_get_drvdata(ccic_device);
|
|
if (!pdic_data) {
|
|
pr_info("pdic_data is null\n");
|
|
return -ENXIO;
|
|
}
|
|
|
|
pd_data = dev_get_drvdata(pdic_data->dev);
|
|
if (!pd_data) {
|
|
pr_info("pd_data is null\n");
|
|
return -ENXIO;
|
|
}
|
|
manager = &pd_data->manager;
|
|
if (!manager) {
|
|
pr_info("manager is null\n");
|
|
return -ENXIO;
|
|
}
|
|
|
|
if (manager->is_samsung_accessory_enter_mode)
|
|
uvdm_ready = true;
|
|
|
|
pr_info("uvdm ready = %d", uvdm_ready);
|
|
return uvdm_ready;
|
|
}
|
|
|
|
void samsung_uvdm_close(void)
|
|
{
|
|
#if defined CONFIG_CCIC_S2MU004
|
|
struct s2mu004_usbpd_data *pdic_data;
|
|
#elif defined CONFIG_CCIC_S2MU106
|
|
struct s2mu106_usbpd_data *pdic_data;
|
|
#elif defined CONFIG_CCIC_S2MU205
|
|
struct s2mu205_usbpd_data *pdic_data;
|
|
#endif
|
|
struct usbpd_data *pd_data;
|
|
struct usbpd_manager_data *manager;
|
|
|
|
pr_info("%s\n", __func__);
|
|
if(!ccic_device) {
|
|
pr_info("ccic_dev is null\n");
|
|
return;
|
|
}
|
|
|
|
pdic_data = dev_get_drvdata(ccic_device);
|
|
if (!pdic_data) {
|
|
pr_info("pdic_data is null\n");
|
|
return;
|
|
}
|
|
|
|
pd_data = dev_get_drvdata(pdic_data->dev);
|
|
if (!pd_data) {
|
|
pr_info("pd_data is null\n");
|
|
return;
|
|
}
|
|
manager = &pd_data->manager;
|
|
if (!manager) {
|
|
pr_info("manager is null\n");
|
|
return;
|
|
}
|
|
|
|
complete(&manager->uvdm_out_wait);
|
|
complete(&manager->uvdm_in_wait);
|
|
|
|
}
|
|
|
|
void set_endian(char *src, char *dest, int size)
|
|
{
|
|
int i, j;
|
|
int loop;
|
|
int dest_pos;
|
|
int src_pos;
|
|
|
|
loop = size / SEC_UVDM_ALIGN;
|
|
loop += (((size % SEC_UVDM_ALIGN) > 0) ? 1:0);
|
|
|
|
for (i = 0 ; i < loop ; i++)
|
|
for (j = 0 ; j < SEC_UVDM_ALIGN ; j++) {
|
|
src_pos = SEC_UVDM_ALIGN * i + j;
|
|
dest_pos = SEC_UVDM_ALIGN * i + SEC_UVDM_ALIGN - j - 1;
|
|
dest[dest_pos] = src[src_pos];
|
|
}
|
|
}
|
|
|
|
int get_checksum(char *data, int start_addr, int size)
|
|
{
|
|
int checksum = 0;
|
|
int i;
|
|
|
|
for (i = 0; i < size; i++)
|
|
checksum += data[start_addr+i];
|
|
|
|
return checksum;
|
|
}
|
|
|
|
int set_uvdmset_count(int size)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (size <= SEC_UVDM_MAXDATA_FIRST)
|
|
ret = 1;
|
|
else {
|
|
ret = ((size-SEC_UVDM_MAXDATA_FIRST) / SEC_UVDM_MAXDATA_NORMAL);
|
|
if (((size-SEC_UVDM_MAXDATA_FIRST) % SEC_UVDM_MAXDATA_NORMAL) == 0)
|
|
ret += 1;
|
|
else
|
|
ret += 2;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void set_msg_header(void *data, int msg_type, int obj_num)
|
|
{
|
|
msg_header_type *msg_hdr;
|
|
uint8_t *SendMSG = (uint8_t *)data;
|
|
|
|
msg_hdr = (msg_header_type *)&SendMSG[0];
|
|
msg_hdr->msg_type = msg_type;
|
|
msg_hdr->num_data_objs = obj_num;
|
|
msg_hdr->port_data_role = USBPD_DFP;
|
|
return;
|
|
|
|
}
|
|
|
|
void set_uvdm_header(void *data, int vid, int vdm_type)
|
|
{
|
|
uvdm_header *uvdm_hdr;
|
|
uint8_t *SendMSG = (uint8_t *)data;
|
|
|
|
uvdm_hdr = (uvdm_header *)&SendMSG[0];
|
|
uvdm_hdr->vendor_id = SAMSUNG_VENDOR_ID;
|
|
uvdm_hdr->vdm_type = vdm_type;
|
|
uvdm_hdr->vendor_defined = SEC_UVDM_UNSTRUCTURED_VDM;
|
|
return;
|
|
}
|
|
|
|
void set_sec_uvdm_header(void *data, int pid, bool data_type, int cmd_type,
|
|
bool dir, int total_set_num, uint8_t received_data)
|
|
{
|
|
s_uvdm_header *SEC_UVDM_HEADER;
|
|
uint8_t *SendMSG = (uint8_t *)data;
|
|
|
|
SEC_UVDM_HEADER = (s_uvdm_header *)&SendMSG[4];
|
|
|
|
SEC_UVDM_HEADER->pid = pid;
|
|
SEC_UVDM_HEADER->data_type = data_type;
|
|
SEC_UVDM_HEADER->cmd_type = cmd_type;
|
|
SEC_UVDM_HEADER->direction = dir;
|
|
SEC_UVDM_HEADER->total_set_num = total_set_num;
|
|
SEC_UVDM_HEADER->data = received_data;
|
|
|
|
pr_info("%s pid = 0x%x data_type=%d ,cmd_type =%d,direction= %d, total_num_of_uvdm_set = %d\n",
|
|
__func__, SEC_UVDM_HEADER->pid,
|
|
SEC_UVDM_HEADER->data_type,
|
|
SEC_UVDM_HEADER->cmd_type,
|
|
SEC_UVDM_HEADER->direction,
|
|
SEC_UVDM_HEADER->total_set_num);
|
|
return;
|
|
}
|
|
|
|
int get_data_size(int first_set, int remained_data_size)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (first_set)
|
|
ret = (remained_data_size <= SEC_UVDM_MAXDATA_FIRST) ? \
|
|
remained_data_size : SEC_UVDM_MAXDATA_FIRST;
|
|
else
|
|
ret = (remained_data_size <= SEC_UVDM_MAXDATA_NORMAL) ? \
|
|
remained_data_size : SEC_UVDM_MAXDATA_NORMAL;
|
|
|
|
return ret;
|
|
}
|
|
|
|
void set_sec_uvdm_tx_header(void *data, int first_set, int cur_set, int total_size,
|
|
int remained_size)
|
|
{
|
|
s_tx_header *SEC_TX_HAEDER;
|
|
uint8_t *SendMSG = (uint8_t*)data;
|
|
|
|
if(first_set)
|
|
SEC_TX_HAEDER = (s_tx_header *)&SendMSG[8];
|
|
else
|
|
SEC_TX_HAEDER = (s_tx_header *)&SendMSG[4];
|
|
SEC_TX_HAEDER->cur_size = get_data_size(first_set,remained_size);
|
|
SEC_TX_HAEDER->total_size = total_size;
|
|
SEC_TX_HAEDER->order_cur_set = cur_set;
|
|
return;
|
|
}
|
|
|
|
void set_sec_uvdm_tx_tailer(void *data)
|
|
{
|
|
s_tx_tailer *SEC_TX_TAILER;
|
|
uint8_t *SendMSG = (uint8_t *)data;
|
|
|
|
SEC_TX_TAILER = (s_tx_tailer *)&SendMSG[24];
|
|
SEC_TX_TAILER->checksum = get_checksum(SendMSG, 4,SEC_UVDM_CHECKSUM_COUNT);
|
|
return;
|
|
}
|
|
|
|
void set_sec_uvdm_rx_header(void *data, int cur_num, int cur_set, int ack)
|
|
{
|
|
s_rx_header *SEC_RX_HEADER;
|
|
uint8_t *SendMSG = (uint8_t*)data;
|
|
|
|
SEC_RX_HEADER = (s_rx_header *)&SendMSG[4];
|
|
SEC_RX_HEADER->order_cur_set = cur_num;
|
|
SEC_RX_HEADER->rcv_data_size = cur_set;
|
|
SEC_RX_HEADER->result_value = ack;
|
|
|
|
return;
|
|
}
|
|
|
|
int usbpd_manager_send_samsung_uvdm_message(void *data, const char *buf, size_t size)
|
|
{
|
|
#if defined CONFIG_CCIC_S2MU004
|
|
struct s2mu004_usbpd_data *pdic_data = data;
|
|
#elif defined CONFIG_CCIC_S2MU106
|
|
struct s2mu106_usbpd_data *pdic_data = data;
|
|
#elif defined CONFIG_CCIC_S2MU205
|
|
struct s2mu205_usbpd_data *pdic_data = data;
|
|
#endif
|
|
struct usbpd_data *pd_data = dev_get_drvdata(pdic_data->dev);
|
|
struct usbpd_manager_data *manager = &pd_data->manager;
|
|
int received_data = 0;
|
|
int data_role = 0;
|
|
int power_role = 0;
|
|
|
|
if ((buf == NULL)||(size < sizeof(unsigned int))) {
|
|
pr_err("%s given data is not valid !\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
sscanf(buf, "%d", &received_data);
|
|
|
|
pd_data->phy_ops.get_data_role(pd_data, &data_role);
|
|
|
|
if (data_role == USBPD_UFP) {
|
|
pr_err("%s, skip, now data role is ufp\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
data_role = pd_data->phy_ops.get_power_role(pd_data, &power_role);
|
|
|
|
manager->uvdm_msg_header.msg_type = USBPD_UVDM_MSG;
|
|
manager->uvdm_msg_header.port_data_role = USBPD_DFP;
|
|
manager->uvdm_msg_header.port_power_role = power_role;
|
|
manager->uvdm_data_obj[0].unstructured_vdm.vendor_id = SAMSUNG_VENDOR_ID;
|
|
manager->uvdm_data_obj[0].unstructured_vdm.vendor_defined = SEC_UVDM_UNSTRUCTURED_VDM;
|
|
manager->uvdm_data_obj[1].object = received_data;
|
|
if (manager->uvdm_data_obj[1].sec_uvdm_header.data_type == SEC_UVDM_SHORT_DATA) {
|
|
pr_info("%s - process short data!\n", __func__);
|
|
// process short data
|
|
// phase 1. fill message header
|
|
manager->uvdm_msg_header.num_data_objs = 2; // VDM Header + 6 VDOs = MAX 7
|
|
// phase 2. fill uvdm header (already filled)
|
|
// phase 3. fill sec uvdm header
|
|
manager->uvdm_data_obj[1].sec_uvdm_header.total_number_of_uvdm_set = 1;
|
|
} else {
|
|
pr_info("%s - process long data!\n", __func__);
|
|
// process long data
|
|
// phase 1. fill message header
|
|
// phase 2. fill uvdm header
|
|
// phase 3. fill sec uvdm header
|
|
|
|
}
|
|
usbpd_manager_inform_event(pd_data, MANAGER_UVDM_SEND_MESSAGE);
|
|
return 0;
|
|
}
|
|
|
|
ssize_t samsung_uvdm_out_request_message(void *data, size_t size)
|
|
{
|
|
#if defined CONFIG_CCIC_S2MU004
|
|
struct s2mu004_usbpd_data *pdic_data;
|
|
#elif defined CONFIG_CCIC_S2MU106
|
|
struct s2mu106_usbpd_data *pdic_data;
|
|
#elif defined CONFIG_CCIC_S2MU205
|
|
struct s2mu205_usbpd_data *pdic_data;
|
|
#endif
|
|
struct usbpd_data *pd_data;
|
|
struct usbpd_manager_data *manager;
|
|
uint8_t *SEC_DATA;
|
|
uint8_t rcv_data[MAX_INPUT_DATA] = {0,};
|
|
int need_set_cnt = 0;
|
|
int cur_set_data = 0;
|
|
int cur_set_num = 0;
|
|
int remained_data_size = 0;
|
|
int accumulated_data_size = 0;
|
|
int received_data_index = 0;
|
|
int time_left = 0;
|
|
int i;
|
|
|
|
pr_info("%s++\n", __func__);
|
|
if(!ccic_device) {
|
|
pr_err("ccic_dev is null\n");
|
|
return -ENXIO;
|
|
}
|
|
|
|
pdic_data = dev_get_drvdata(ccic_device);
|
|
if (!pdic_data) {
|
|
pr_err("pdic_data is null\n");
|
|
return -ENXIO;
|
|
}
|
|
|
|
pd_data = dev_get_drvdata(pdic_data->dev);
|
|
if (!pd_data) {
|
|
pr_err("pd_data is null\n");
|
|
return -ENXIO;
|
|
}
|
|
manager = &pd_data->manager;
|
|
if (!manager) {
|
|
pr_err("manager is null\n");
|
|
return -ENXIO;
|
|
}
|
|
|
|
pr_info("%s \n", __func__);
|
|
set_msg_header(&manager->uvdm_msg_header,USBPD_Vendor_Defined,7);
|
|
set_uvdm_header(&manager->uvdm_data_obj[0], SAMSUNG_VENDOR_ID, 0);
|
|
|
|
if (size <= 1) {
|
|
pr_info("%s - process short data!\n", __func__);
|
|
// process short data
|
|
// phase 1. fill message header
|
|
manager->uvdm_msg_header.num_data_objs = 2; // VDM Header + 6 VDOs = MAX 7
|
|
// phase 2. fill uvdm header (already filled)
|
|
// phase 3. fill sec uvdm header
|
|
manager->uvdm_data_obj[1].sec_uvdm_header.total_number_of_uvdm_set = 1;
|
|
} else {
|
|
pr_info("%s - process long data!\n", __func__);
|
|
// process long data
|
|
// phase 1. fill message header
|
|
// phase 2. fill uvdm header
|
|
// phase 3. fill sec uvdm header
|
|
// phase 4.5.6.7 fill sec data header , data , sec data tail and so on.
|
|
|
|
set_endian(data, rcv_data, size);
|
|
need_set_cnt = set_uvdmset_count(size);
|
|
manager->uvdm_first_req = true;
|
|
manager->uvdm_dir = DIR_OUT;
|
|
cur_set_num = 1;
|
|
accumulated_data_size = 0;
|
|
remained_data_size = size;
|
|
|
|
if (manager->uvdm_first_req)
|
|
set_sec_uvdm_header(&manager->uvdm_data_obj[0], manager->Product_ID,
|
|
SEC_UVDM_LONG_DATA,SEC_UVDM_ININIATOR, DIR_OUT,
|
|
need_set_cnt, 0);
|
|
while (cur_set_num <= need_set_cnt) {
|
|
cur_set_data = 0;
|
|
time_left = 0;
|
|
set_sec_uvdm_tx_header(&manager->uvdm_data_obj[0], manager->uvdm_first_req,
|
|
cur_set_num, size, remained_data_size);
|
|
cur_set_data = get_data_size(manager->uvdm_first_req,remained_data_size);
|
|
|
|
pr_info("%s current set data size: %d, total data size %ld, current uvdm set num %d\n", __func__, cur_set_data, size, cur_set_num);
|
|
|
|
if (manager->uvdm_first_req) {
|
|
SEC_DATA = (uint8_t *)&manager->uvdm_data_obj[3];
|
|
for ( i = 0; i < SEC_UVDM_MAXDATA_FIRST; i++)
|
|
SEC_DATA[i] = rcv_data[received_data_index++];
|
|
} else {
|
|
SEC_DATA = (uint8_t *)&manager->uvdm_data_obj[2];
|
|
for ( i = 0; i < SEC_UVDM_MAXDATA_NORMAL; i++)
|
|
SEC_DATA[i] = rcv_data[received_data_index++];
|
|
}
|
|
|
|
set_sec_uvdm_tx_tailer(&manager->uvdm_data_obj[0]);
|
|
|
|
reinit_completion(&manager->uvdm_out_wait);
|
|
usbpd_manager_inform_event(pd_data, MANAGER_UVDM_SEND_MESSAGE);
|
|
|
|
time_left = wait_for_completion_interruptible_timeout(&manager->uvdm_out_wait, msecs_to_jiffies(SEC_UVDM_WAIT_MS));
|
|
if (time_left <= 0) {
|
|
pr_err("%s tiemout \n",__func__);
|
|
return -ETIME;
|
|
}
|
|
accumulated_data_size += cur_set_data;
|
|
remained_data_size -= cur_set_data;
|
|
if (manager->uvdm_first_req)
|
|
manager->uvdm_first_req = false;
|
|
cur_set_num++;
|
|
}
|
|
return size;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int samsung_uvdm_in_request_message(void *data)
|
|
{
|
|
#if defined CONFIG_CCIC_S2MU004
|
|
struct s2mu004_usbpd_data *pdic_data;
|
|
#elif defined CONFIG_CCIC_S2MU106
|
|
struct s2mu106_usbpd_data *pdic_data;
|
|
#elif defined CONFIG_CCIC_S2MU205
|
|
struct s2mu205_usbpd_data *pdic_data;
|
|
#endif
|
|
struct usbpd_data *pd_data;
|
|
struct usbpd_manager_data *manager;
|
|
struct policy_data *policy;
|
|
uint8_t in_data[MAX_INPUT_DATA] = {0,};
|
|
|
|
s_uvdm_header SEC_RES_HEADER;
|
|
s_tx_header SEC_TX_HEADER;
|
|
s_tx_tailer SEC_TX_TAILER;
|
|
data_obj_type uvdm_data_obj[USBPD_MAX_COUNT_MSG_OBJECT];
|
|
msg_header_type uvdm_msg_header;
|
|
|
|
int cur_set_data = 0;
|
|
int cur_set_num = 0;
|
|
int total_set_num = 0;
|
|
int rcv_data_size = 0;
|
|
int total_rcv_size = 0;
|
|
int ack = 0;
|
|
int size = 0;
|
|
int time_left = 0;
|
|
int i;
|
|
int cal_checksum = 0;
|
|
|
|
pr_info("%s\n", __func__);
|
|
|
|
if(!ccic_device)
|
|
return -ENXIO;
|
|
|
|
pdic_data = dev_get_drvdata(ccic_device);
|
|
if (!pdic_data) {
|
|
pr_err("pdic_data is null\n");
|
|
return -ENXIO;
|
|
}
|
|
|
|
pd_data = dev_get_drvdata(pdic_data->dev);
|
|
if (!pd_data)
|
|
return -ENXIO;
|
|
|
|
manager = &pd_data->manager;
|
|
if (!manager)
|
|
return -ENXIO;
|
|
policy = &pd_data->policy;
|
|
if (!policy)
|
|
return -ENXIO;
|
|
|
|
manager->uvdm_dir = DIR_IN;
|
|
manager->uvdm_first_req = true;
|
|
uvdm_msg_header.word = policy->rx_msg_header.word;
|
|
|
|
/* 2. Common : Fill the MSGHeader */
|
|
set_msg_header(&manager->uvdm_msg_header, USBPD_Vendor_Defined, 2);
|
|
/* 3. Common : Fill the UVDMHeader*/
|
|
set_uvdm_header(&manager->uvdm_data_obj[0], SAMSUNG_VENDOR_ID, 0);
|
|
|
|
/* 4. Common : Fill the First SEC_VDMHeader*/
|
|
if(manager->uvdm_first_req)
|
|
set_sec_uvdm_header(&manager->uvdm_data_obj[0], manager->Product_ID,\
|
|
SEC_UVDM_LONG_DATA, SEC_UVDM_ININIATOR, DIR_IN, 0, 0);
|
|
|
|
/* 5. Send data to PDIC */
|
|
reinit_completion(&manager->uvdm_in_wait);
|
|
usbpd_manager_inform_event(pd_data, MANAGER_UVDM_SEND_MESSAGE);
|
|
|
|
cur_set_num = 0;
|
|
total_set_num = 1;
|
|
|
|
do {
|
|
time_left =
|
|
wait_for_completion_interruptible_timeout(&manager->uvdm_in_wait,
|
|
msecs_to_jiffies(SEC_UVDM_WAIT_MS));
|
|
if (time_left <= 0) {
|
|
pr_err("%s timeout\n", __func__);
|
|
return -ETIME;
|
|
}
|
|
|
|
/* read data */
|
|
uvdm_msg_header.word = policy->rx_msg_header.word;
|
|
for (i = 0; i < uvdm_msg_header.num_data_objs; i++)
|
|
uvdm_data_obj[i].object = policy->rx_data_obj[i].object;
|
|
|
|
if (manager->uvdm_first_req) {
|
|
SEC_RES_HEADER.object = uvdm_data_obj[1].object;
|
|
SEC_TX_HEADER.object = uvdm_data_obj[2].object;
|
|
|
|
if (SEC_RES_HEADER.data_type == TYPE_SHORT) {
|
|
in_data[rcv_data_size++] = SEC_RES_HEADER.data;
|
|
return rcv_data_size;
|
|
} else {
|
|
/* 1. check the data size received */
|
|
size = SEC_TX_HEADER.total_size;
|
|
cur_set_data = SEC_TX_HEADER.cur_size;
|
|
cur_set_num = SEC_TX_HEADER.order_cur_set;
|
|
total_set_num = SEC_RES_HEADER.total_set_num;
|
|
|
|
manager->uvdm_first_req = false;
|
|
/* 2. copy data to buffer */
|
|
for (i = 0; i < SEC_UVDM_MAXDATA_FIRST; i++) {
|
|
in_data[rcv_data_size++] =uvdm_data_obj[3+i/SEC_UVDM_ALIGN].byte[i%SEC_UVDM_ALIGN];
|
|
}
|
|
total_rcv_size += cur_set_data;
|
|
manager->uvdm_first_req = false;
|
|
}
|
|
} else {
|
|
SEC_TX_HEADER.object = uvdm_data_obj[1].object;
|
|
cur_set_data = SEC_TX_HEADER.cur_size;
|
|
cur_set_num = SEC_TX_HEADER.order_cur_set;
|
|
/* 2. copy data to buffer */
|
|
for (i = 0 ; i < SEC_UVDM_MAXDATA_NORMAL; i++)
|
|
in_data[rcv_data_size++] = uvdm_data_obj[2+i/SEC_UVDM_ALIGN].byte[i%SEC_UVDM_ALIGN];
|
|
total_rcv_size += cur_set_data;
|
|
}
|
|
/* 3. Check Checksum */
|
|
SEC_TX_TAILER.object =uvdm_data_obj[6].object;
|
|
cal_checksum = get_checksum((char *)&uvdm_data_obj[0], 4, SEC_UVDM_CHECKSUM_COUNT);
|
|
ack = (cal_checksum == SEC_TX_TAILER.checksum) ? RX_ACK : RX_NAK;
|
|
|
|
/* 5. Common : Fill the MSGHeader */
|
|
set_msg_header(&manager->uvdm_msg_header, USBPD_Vendor_Defined, 2);
|
|
/* 5.1. Common : Fill the UVDMHeader*/
|
|
set_uvdm_header(&manager->uvdm_data_obj[0], SAMSUNG_VENDOR_ID, 0);
|
|
/* 5.2. Common : Fill the First SEC_VDMHeader*/
|
|
|
|
set_sec_uvdm_rx_header(&manager->uvdm_data_obj[0], cur_set_num, cur_set_data, ack);
|
|
reinit_completion(&manager->uvdm_in_wait);
|
|
usbpd_manager_inform_event(pd_data, MANAGER_UVDM_SEND_MESSAGE);
|
|
} while ( cur_set_num < total_set_num);
|
|
|
|
set_endian(in_data, data, size);
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
void usbpd_manager_receive_samsung_uvdm_message(struct usbpd_data *pd_data)
|
|
{
|
|
struct policy_data *policy = &pd_data->policy;
|
|
int i = 0;
|
|
msg_header_type uvdm_msg_header;
|
|
data_obj_type uvdm_data_obj[USBPD_MAX_COUNT_MSG_OBJECT];
|
|
struct usbpd_manager_data *manager = &pd_data->manager;
|
|
s_uvdm_header SEC_UVDM_RES_HEADER;
|
|
//s_uvdm_header SEC_UVDM_HEADER;
|
|
s_rx_header SEC_UVDM_RX_HEADER;
|
|
uvdm_msg_header.word = policy->rx_msg_header.word;
|
|
|
|
|
|
for (i = 0; i < uvdm_msg_header.num_data_objs; i++)
|
|
uvdm_data_obj[i].object = policy->rx_data_obj[i].object;
|
|
|
|
uvdm_msg_header.word = policy->rx_msg_header.word;
|
|
|
|
pr_info("%s dir %s \n", __func__, (manager->uvdm_dir==DIR_OUT)?"OUT":"IN");
|
|
if (manager->uvdm_dir == DIR_OUT) {
|
|
if (manager->uvdm_first_req) {
|
|
SEC_UVDM_RES_HEADER.object = uvdm_data_obj[1].object;
|
|
if (SEC_UVDM_RES_HEADER.data_type == TYPE_LONG) {
|
|
if (SEC_UVDM_RES_HEADER.cmd_type == RES_ACK) {
|
|
SEC_UVDM_RX_HEADER.object = uvdm_data_obj[2].object;
|
|
if (SEC_UVDM_RX_HEADER.result_value != RX_ACK)
|
|
pr_err("%s Busy or Nak received.\n", __func__);
|
|
} else
|
|
pr_err("%s Response type is wrong.\n", __func__);
|
|
} else {
|
|
if ( SEC_UVDM_RES_HEADER.cmd_type == RES_ACK)
|
|
pr_err("%s Short packet: ack received\n", __func__);
|
|
else
|
|
pr_err("%s Short packet: Response type is wrong\n", __func__);
|
|
}
|
|
/* Dir: out */
|
|
} else {
|
|
SEC_UVDM_RX_HEADER.object = uvdm_data_obj[1].object;
|
|
if (SEC_UVDM_RX_HEADER.result_value != RX_ACK)
|
|
pr_err("%s Busy or Nak received.\n", __func__);
|
|
}
|
|
complete(&manager->uvdm_out_wait);
|
|
} else {
|
|
if (manager->uvdm_first_req) {
|
|
SEC_UVDM_RES_HEADER.object = uvdm_data_obj[1].object;
|
|
if (SEC_UVDM_RES_HEADER.cmd_type != RES_ACK) {
|
|
pr_err("%s Busy or Nak received.\n", __func__);
|
|
return;
|
|
}
|
|
}
|
|
|
|
complete(&manager->uvdm_in_wait);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void usbpd_manager_plug_attach(struct device *dev, muic_attached_dev_t new_dev)
|
|
{
|
|
#ifdef CONFIG_BATTERY_SAMSUNG
|
|
#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER
|
|
struct usbpd_data *pd_data = dev_get_drvdata(dev);
|
|
struct policy_data *policy = &pd_data->policy;
|
|
struct usbpd_manager_data *manager = &pd_data->manager;
|
|
|
|
CC_NOTI_ATTACH_TYPEDEF pd_notifier;
|
|
|
|
if (new_dev == ATTACHED_DEV_TYPE3_CHARGER_MUIC) {
|
|
if (policy->send_sink_cap || (manager->ps_rdy == 1 &&
|
|
manager->prev_available_pdo != pd_noti.sink_status.available_pdo_num)) {
|
|
pd_noti.event = PDIC_NOTIFY_EVENT_PD_SINK_CAP;
|
|
policy->send_sink_cap = 0;
|
|
} else
|
|
pd_noti.event = PDIC_NOTIFY_EVENT_PD_SINK;
|
|
manager->ps_rdy = 1;
|
|
manager->prev_available_pdo = pd_noti.sink_status.available_pdo_num;
|
|
pd_notifier.src = CCIC_NOTIFY_DEV_CCIC;
|
|
pd_notifier.dest = CCIC_NOTIFY_DEV_BATTERY;
|
|
pd_notifier.id = CCIC_NOTIFY_ID_POWER_STATUS;
|
|
pd_notifier.attach = 1;
|
|
pd_notifier.pd = &pd_noti;
|
|
#if defined(CONFIG_CCIC_NOTIFIER)
|
|
ccic_notifier_notify((CC_NOTI_TYPEDEF *)&pd_notifier, &pd_noti, 1/* pdic_attach */);
|
|
#endif
|
|
}
|
|
|
|
#else
|
|
struct usbpd_data *pd_data = dev_get_drvdata(dev);
|
|
struct usbpd_manager_data *manager = &pd_data->manager;
|
|
|
|
pr_info("%s: usbpd plug attached\n", __func__);
|
|
manager->attached_dev = new_dev;
|
|
s2m_pdic_notifier_attach_attached_dev(manager->attached_dev);
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
void usbpd_manager_plug_detach(struct device *dev, bool notify)
|
|
{
|
|
struct usbpd_data *pd_data = dev_get_drvdata(dev);
|
|
struct usbpd_manager_data *manager = &pd_data->manager;
|
|
|
|
pr_info("%s: usbpd plug detached\n", __func__);
|
|
|
|
usbpd_policy_reset(pd_data, PLUG_DETACHED);
|
|
if (notify)
|
|
s2m_pdic_notifier_detach_attached_dev(manager->attached_dev);
|
|
manager->attached_dev = ATTACHED_DEV_NONE_MUIC;
|
|
}
|
|
|
|
void usbpd_manager_acc_detach(struct device *dev)
|
|
{
|
|
struct usbpd_data *pd_data = dev_get_drvdata(dev);
|
|
struct usbpd_manager_data *manager = &pd_data->manager;
|
|
|
|
pr_info("%s\n", __func__);
|
|
if ( manager->acc_type != CCIC_DOCK_DETACHED ) {
|
|
pr_info("%s: schedule_delayed_work \n", __func__);
|
|
if ( manager->acc_type == CCIC_DOCK_HMT )
|
|
schedule_delayed_work(&manager->acc_detach_handler, msecs_to_jiffies(1000));
|
|
else
|
|
schedule_delayed_work(&manager->acc_detach_handler, msecs_to_jiffies(0));
|
|
}
|
|
}
|
|
static int usbpd_manager_support_vdm(struct usbpd_data *pd_data,
|
|
usbpd_manager_command_type command)
|
|
{
|
|
struct usbpd_manager_data *manager = &pd_data->manager;
|
|
|
|
switch (command) {
|
|
case MANAGER_REQ_VDM_DISCOVER_SVID:
|
|
case MANAGER_REQ_VDM_DISCOVER_MODE:
|
|
case MANAGER_REQ_VDM_ENTER_MODE:
|
|
case MANAGER_REQ_VDM_STATUS_UPDATE:
|
|
case MANAGER_REQ_VDM_DisplayPort_Configure:
|
|
if (manager->Vendor_ID == SAMSUNG_VENDOR_ID) {
|
|
pr_info("%s, Discover ID, VendorID == SAMSUNG \n", __func__);
|
|
return 1;
|
|
}
|
|
#if defined(CONFIG_CCIC_ALTERNATE_MODE)
|
|
return 1;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
default:
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
|
|
int usbpd_manager_command_to_policy(struct device *dev,
|
|
usbpd_manager_command_type command)
|
|
{
|
|
struct usbpd_data *pd_data = dev_get_drvdata(dev);
|
|
struct usbpd_manager_data *manager = &pd_data->manager;
|
|
|
|
if (usbpd_manager_support_vdm(pd_data, command)) {
|
|
manager->cmd |= command;
|
|
usbpd_kick_policy_work(dev);
|
|
}
|
|
|
|
/* TODO: check result
|
|
if (manager->event) {
|
|
...
|
|
}
|
|
*/
|
|
return 0;
|
|
}
|
|
|
|
void usbpd_manager_inform_event(struct usbpd_data *pd_data,
|
|
usbpd_manager_event_type event)
|
|
{
|
|
struct usbpd_manager_data *manager = &pd_data->manager;
|
|
|
|
manager->event = event;
|
|
|
|
switch (event) {
|
|
case MANAGER_DISCOVER_IDENTITY_ACKED:
|
|
usbpd_manager_get_identity(pd_data);
|
|
usbpd_manager_command_to_policy(pd_data->dev,
|
|
MANAGER_REQ_VDM_DISCOVER_SVID);
|
|
break;
|
|
case MANAGER_DISCOVER_SVID_ACKED:
|
|
usbpd_manager_get_svids(pd_data);
|
|
usbpd_manager_command_to_policy(pd_data->dev,
|
|
MANAGER_REQ_VDM_DISCOVER_MODE);
|
|
break;
|
|
case MANAGER_DISCOVER_MODE_ACKED:
|
|
usbpd_manager_get_modes(pd_data);
|
|
usbpd_manager_command_to_policy(pd_data->dev,
|
|
MANAGER_REQ_VDM_ENTER_MODE);
|
|
break;
|
|
case MANAGER_ENTER_MODE_ACKED:
|
|
usbpd_manager_enter_mode(pd_data);
|
|
usbpd_manager_command_to_policy(pd_data->dev,
|
|
MANAGER_REQ_VDM_STATUS_UPDATE);
|
|
break;
|
|
case MANAGER_STATUS_UPDATE_ACKED:
|
|
usbpd_manager_command_to_policy(pd_data->dev,
|
|
MANAGER_REQ_VDM_DisplayPort_Configure);
|
|
break;
|
|
case MANAGER_DisplayPort_Configure_ACKED:
|
|
break;
|
|
case MANAGER_NEW_POWER_SRC:
|
|
usbpd_manager_command_to_policy(pd_data->dev,
|
|
MANAGER_REQ_NEW_POWER_SRC);
|
|
break;
|
|
case MANAGER_GET_SRC_CAP:
|
|
usbpd_manager_command_to_policy(pd_data->dev,
|
|
MANAGER_REQ_GET_SRC_CAP);
|
|
break;
|
|
case MANAGER_UVDM_SEND_MESSAGE:
|
|
usbpd_manager_command_to_policy(pd_data->dev,
|
|
MANAGER_REQ_UVDM_SEND_MESSAGE);
|
|
break;
|
|
case MANAGER_UVDM_RECEIVE_MESSAGE:
|
|
usbpd_manager_receive_samsung_uvdm_message(pd_data);
|
|
break;
|
|
case MANAGER_START_DISCOVER_IDENTITY:
|
|
usbpd_manager_command_to_policy(pd_data->dev,
|
|
MANAGER_REQ_VDM_DISCOVER_IDENTITY);
|
|
break;
|
|
case MANAGER_SEND_PR_SWAP:
|
|
usbpd_manager_command_to_policy(pd_data->dev,
|
|
MANAGER_REQ_PR_SWAP);
|
|
break;
|
|
case MANAGER_SEND_DR_SWAP:
|
|
usbpd_manager_command_to_policy(pd_data->dev,
|
|
MANAGER_REQ_DR_SWAP);
|
|
break;
|
|
default:
|
|
pr_info("%s: not matched event(%d)\n", __func__, event);
|
|
}
|
|
}
|
|
|
|
bool usbpd_manager_vdm_request_enabled(struct usbpd_data *pd_data)
|
|
{
|
|
struct usbpd_manager_data *manager = &pd_data->manager;
|
|
/* TODO : checking cable discovering
|
|
if (pd_data->counter.discover_identity_counter
|
|
< USBPD_nDiscoverIdentityCount)
|
|
|
|
struct usbpd_manager_data *manager = &pd_data->manager;
|
|
if (manager->event != MANAGER_DISCOVER_IDENTITY_ACKED
|
|
|| manager->event != MANAGER_DISCOVER_IDENTITY_NAKED)
|
|
|
|
return(1);
|
|
*/
|
|
|
|
manager->vdm_en = 1;
|
|
#if 1
|
|
if (manager->alt_sended == 0 && manager->vdm_en == 1) {
|
|
usbpd_manager_inform_event(pd_noti.pd_data,
|
|
MANAGER_START_DISCOVER_IDENTITY);
|
|
manager->alt_sended = 1;
|
|
}
|
|
#else
|
|
schedule_delayed_work(&manager->start_discover_msg_handler, msecs_to_jiffies(30));
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
bool usbpd_manager_power_role_swap(struct usbpd_data *pd_data)
|
|
{
|
|
struct usbpd_manager_data *manager = &pd_data->manager;
|
|
|
|
return manager->power_role_swap;
|
|
}
|
|
|
|
bool usbpd_manager_vconn_source_swap(struct usbpd_data *pd_data)
|
|
{
|
|
struct usbpd_manager_data *manager = &pd_data->manager;
|
|
|
|
return manager->vconn_source_swap;
|
|
}
|
|
|
|
void usbpd_manager_turn_off_vconn(struct usbpd_data *pd_data)
|
|
{
|
|
/* TODO : Turn off vconn */
|
|
}
|
|
|
|
void usbpd_manager_turn_on_source(struct usbpd_data *pd_data)
|
|
{
|
|
struct usbpd_manager_data *manager = &pd_data->manager;
|
|
|
|
pr_info("%s: usbpd plug attached\n", __func__);
|
|
|
|
manager->attached_dev = ATTACHED_DEV_TYPE3_ADAPTER_MUIC;
|
|
s2m_pdic_notifier_attach_attached_dev(manager->attached_dev);
|
|
/* TODO : Turn on source */
|
|
}
|
|
|
|
void usbpd_manager_turn_off_power_supply(struct usbpd_data *pd_data)
|
|
{
|
|
struct usbpd_manager_data *manager = &pd_data->manager;
|
|
|
|
pr_info("%s: usbpd plug detached\n", __func__);
|
|
|
|
s2m_pdic_notifier_detach_attached_dev(manager->attached_dev);
|
|
manager->attached_dev = ATTACHED_DEV_NONE_MUIC;
|
|
/* TODO : Turn off power supply */
|
|
}
|
|
|
|
void usbpd_manager_turn_off_power_sink(struct usbpd_data *pd_data)
|
|
{
|
|
struct usbpd_manager_data *manager = &pd_data->manager;
|
|
|
|
pr_info("%s: usbpd sink turn off\n", __func__);
|
|
|
|
s2m_pdic_notifier_detach_attached_dev(manager->attached_dev);
|
|
manager->attached_dev = ATTACHED_DEV_NONE_MUIC;
|
|
/* TODO : Turn off power sink */
|
|
}
|
|
|
|
bool usbpd_manager_data_role_swap(struct usbpd_data *pd_data)
|
|
{
|
|
struct usbpd_manager_data *manager = &pd_data->manager;
|
|
|
|
return manager->data_role_swap;
|
|
}
|
|
|
|
int usbpd_manager_register_switch_device(int mode)
|
|
{
|
|
#ifdef CONFIG_SWITCH
|
|
int ret = 0;
|
|
if (mode) {
|
|
ret = switch_dev_register(&switch_dock);
|
|
if (ret < 0) {
|
|
pr_err("%s: Failed to register dock switch(%d)\n",
|
|
__func__, ret);
|
|
return -ENODEV;
|
|
}
|
|
} else {
|
|
switch_dev_unregister(&switch_dock);
|
|
}
|
|
#endif /* CONFIG_SWITCH */
|
|
return 0;
|
|
}
|
|
|
|
static void usbpd_manager_send_dock_intent(int type)
|
|
{
|
|
pr_info("%s: CCIC dock type(%d)\n", __func__, type);
|
|
#ifdef CONFIG_SWITCH
|
|
switch_set_state(&switch_dock, type);
|
|
#endif /* CONFIG_SWITCH */
|
|
}
|
|
|
|
void usbpd_manager_send_dock_uevent(u32 vid, u32 pid, int state)
|
|
{
|
|
char switch_string[32];
|
|
char pd_ids_string[32];
|
|
char *envp[3] = { switch_string, pd_ids_string, NULL };
|
|
|
|
pr_info("%s: CCIC dock : USBPD_IPS=%04x:%04x SWITCH_STATE=%d\n",
|
|
__func__,
|
|
le16_to_cpu(vid),
|
|
le16_to_cpu(pid),
|
|
state);
|
|
|
|
if (IS_ERR(ccic_device)) {
|
|
pr_err("%s CCIC ERROR: Failed to send a dock uevent!\n",
|
|
__func__);
|
|
return;
|
|
}
|
|
|
|
snprintf(switch_string, 32, "SWITCH_STATE=%d", state);
|
|
snprintf(pd_ids_string, 32, "USBPD_IDS=%04x:%04x",
|
|
le16_to_cpu(vid),
|
|
le16_to_cpu(pid));
|
|
kobject_uevent_env(&ccic_device->kobj, KOBJ_CHANGE, envp);
|
|
}
|
|
|
|
void usbpd_manager_acc_detach_handler(struct work_struct *wk)
|
|
{
|
|
// struct delayed_work *delay_work =
|
|
// container_of(wk, struct delayed_work, work);
|
|
struct usbpd_manager_data *manager =
|
|
container_of(wk, struct usbpd_manager_data, acc_detach_handler.work);
|
|
|
|
pr_info("%s: attached_dev : %d ccic dock type %d\n", __func__, manager->attached_dev, manager->acc_type);
|
|
if (manager->attached_dev == ATTACHED_DEV_NONE_MUIC) {
|
|
if (manager->acc_type != CCIC_DOCK_DETACHED) {
|
|
if (manager->acc_type != CCIC_DOCK_NEW)
|
|
usbpd_manager_send_dock_intent(CCIC_DOCK_DETACHED);
|
|
usbpd_manager_send_dock_uevent(manager->Vendor_ID, manager->Product_ID,
|
|
CCIC_DOCK_DETACHED);
|
|
manager->acc_type = CCIC_DOCK_DETACHED;
|
|
manager->Vendor_ID = 0;
|
|
manager->Product_ID = 0;
|
|
manager->is_samsung_accessory_enter_mode = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
void usbpd_manager_acc_handler_cancel(struct device *dev)
|
|
{
|
|
struct usbpd_data *pd_data = dev_get_drvdata(dev);
|
|
struct usbpd_manager_data *manager = &pd_data->manager;
|
|
|
|
if (manager->acc_type != CCIC_DOCK_DETACHED) {
|
|
pr_info("%s: cancel_delayed_work_sync \n", __func__);
|
|
cancel_delayed_work_sync(&manager->acc_detach_handler);
|
|
}
|
|
}
|
|
|
|
static int usbpd_manager_check_accessory(struct usbpd_manager_data *manager)
|
|
{
|
|
#if defined(CONFIG_USB_HW_PARAM)
|
|
struct otg_notify *o_notify = get_otg_notify();
|
|
#endif
|
|
uint16_t vid = manager->Vendor_ID;
|
|
uint16_t pid = manager->Product_ID;
|
|
uint16_t acc_type = CCIC_DOCK_DETACHED;
|
|
|
|
/* detect Gear VR */
|
|
if (manager->acc_type == CCIC_DOCK_DETACHED) {
|
|
if (vid == SAMSUNG_VENDOR_ID) {
|
|
switch (pid) {
|
|
/* GearVR: Reserved GearVR PID+6 */
|
|
case GEARVR_PRODUCT_ID:
|
|
case GEARVR_PRODUCT_ID_1:
|
|
case GEARVR_PRODUCT_ID_2:
|
|
case GEARVR_PRODUCT_ID_3:
|
|
case GEARVR_PRODUCT_ID_4:
|
|
case GEARVR_PRODUCT_ID_5:
|
|
acc_type = CCIC_DOCK_HMT;
|
|
pr_info("%s : Samsung Gear VR connected.\n", __func__);
|
|
#if defined(CONFIG_USB_HW_PARAM)
|
|
if (o_notify)
|
|
inc_hw_param(o_notify, USB_CCIC_VR_USE_COUNT);
|
|
#endif
|
|
break;
|
|
case DEXDOCK_PRODUCT_ID:
|
|
acc_type = CCIC_DOCK_DEX;
|
|
pr_info("%s : Samsung DEX connected.\n", __func__);
|
|
#if defined(CONFIG_USB_HW_PARAM)
|
|
if (o_notify)
|
|
inc_hw_param(o_notify, USB_CCIC_DEX_USE_COUNT);
|
|
#endif
|
|
break;
|
|
case HDMI_PRODUCT_ID:
|
|
acc_type = CCIC_DOCK_HDMI;
|
|
pr_info("%s : Samsung HDMI connected.\n", __func__);
|
|
break;
|
|
default:
|
|
acc_type = CCIC_DOCK_NEW;
|
|
pr_info("%s : default device connected.\n", __func__);
|
|
break;
|
|
}
|
|
} else if (vid == SAMSUNG_MPA_VENDOR_ID) {
|
|
switch(pid) {
|
|
case MPA_PRODUCT_ID:
|
|
acc_type = CCIC_DOCK_MPA;
|
|
pr_info("%s : Samsung MPA connected.\n", __func__);
|
|
break;
|
|
default:
|
|
acc_type = CCIC_DOCK_NEW;
|
|
pr_info("%s : default device connected.\n", __func__);
|
|
break;
|
|
}
|
|
}
|
|
manager->acc_type = acc_type;
|
|
} else
|
|
acc_type = manager->acc_type;
|
|
|
|
if (acc_type != CCIC_DOCK_NEW)
|
|
usbpd_manager_send_dock_intent(acc_type);
|
|
|
|
usbpd_manager_send_dock_uevent(vid, pid, acc_type);
|
|
return 1;
|
|
}
|
|
|
|
/* Ok : 0, NAK: -1 */
|
|
int usbpd_manager_get_identity(struct usbpd_data *pd_data)
|
|
{
|
|
struct policy_data *policy = &pd_data->policy;
|
|
struct usbpd_manager_data *manager = &pd_data->manager;
|
|
|
|
manager->Vendor_ID = policy->rx_data_obj[1].id_header_vdo.USB_Vendor_ID;
|
|
manager->Product_ID = policy->rx_data_obj[3].product_vdo.USB_Product_ID;
|
|
manager->Device_Version = policy->rx_data_obj[3].product_vdo.Device_Version;
|
|
|
|
pr_info("%s, Vendor_ID : 0x%x, Product_ID : 0x%x, Device Version : 0x%x\n",
|
|
__func__, manager->Vendor_ID, manager->Product_ID, manager->Device_Version);
|
|
|
|
if (usbpd_manager_check_accessory(manager))
|
|
pr_info("%s, Samsung Accessory Connected.\n", __func__);
|
|
|
|
return MANAGER_SUPPORT;
|
|
}
|
|
|
|
/* Ok : 0 (SVID_0 is DP support(0xff01)), NAK: -1 */
|
|
int usbpd_manager_get_svids(struct usbpd_data *pd_data)
|
|
{
|
|
struct policy_data *policy = &pd_data->policy;
|
|
struct usbpd_manager_data *manager = &pd_data->manager;
|
|
|
|
manager->SVID_0 = policy->rx_data_obj[1].vdm_svid.svid_0;
|
|
manager->SVID_1 = policy->rx_data_obj[1].vdm_svid.svid_1;
|
|
|
|
pr_info("%s, SVID_0 : 0x%x, SVID_1 : 0x%x\n", __func__,
|
|
manager->SVID_0, manager->SVID_1);
|
|
|
|
#if defined(CONFIG_CCIC_ALTERNATE_MODE)
|
|
if (manager->SVID_0 == TypeC_DP_SUPPORT)
|
|
return MANAGER_SUPPORT;
|
|
#endif
|
|
|
|
return MANAGER_NOT_SUPPORT;
|
|
}
|
|
|
|
/* Ok : 0 (SVID_0 is DP support(0xff01)), NAK: -1 */
|
|
int usbpd_manager_get_modes(struct usbpd_data *pd_data)
|
|
{
|
|
struct policy_data *policy = &pd_data->policy;
|
|
struct usbpd_manager_data *manager = &pd_data->manager;
|
|
|
|
manager->Standard_Vendor_ID = policy->rx_data_obj[0].structured_vdm.svid;
|
|
|
|
pr_info("%s, Standard_Vendor_ID = 0x%x\n", __func__,
|
|
manager->Standard_Vendor_ID);
|
|
#if defined(CONFIG_CCIC_ALTERNATE_MODE)
|
|
return MANAGER_SUPPORT;
|
|
#else
|
|
return MANAGER_NOT_SUPPORT;
|
|
#endif
|
|
}
|
|
|
|
int usbpd_manager_enter_mode(struct usbpd_data *pd_data)
|
|
{
|
|
struct policy_data *policy = &pd_data->policy;
|
|
struct usbpd_manager_data *manager = &pd_data->manager;
|
|
manager->Standard_Vendor_ID = policy->rx_data_obj[0].structured_vdm.svid;
|
|
manager->is_samsung_accessory_enter_mode = true;
|
|
return 0;
|
|
}
|
|
|
|
int usbpd_manager_exit_mode(struct usbpd_data *pd_data, unsigned mode)
|
|
{
|
|
#if defined(CONFIG_CCIC_ALTERNATE_MODE)
|
|
return MANAGER_SUPPORT;
|
|
#else
|
|
return MANAGER_NOT_SUPPORT;
|
|
#endif
|
|
}
|
|
|
|
data_obj_type usbpd_manager_select_capability(struct usbpd_data *pd_data)
|
|
{
|
|
/* TODO: Request from present capabilities
|
|
indicate if other capabilities would be required */
|
|
data_obj_type obj;
|
|
#ifdef CONFIG_BATTERY_SAMSUNG
|
|
#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER
|
|
int pdo_num = pd_noti.sink_status.selected_pdo_num;
|
|
#endif
|
|
#endif
|
|
obj.request_data_object.no_usb_suspend = 1;
|
|
obj.request_data_object.usb_comm_capable = 1;
|
|
obj.request_data_object.capability_mismatch = 0;
|
|
obj.request_data_object.give_back = 0;
|
|
#ifdef CONFIG_BATTERY_SAMSUNG
|
|
#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER
|
|
obj.request_data_object.min_current = pd_noti.sink_status.power_list[pdo_num].max_current / USBPD_CURRENT_UNIT;
|
|
obj.request_data_object.op_current = pd_noti.sink_status.power_list[pdo_num].max_current / USBPD_CURRENT_UNIT;
|
|
obj.request_data_object.object_position = pd_noti.sink_status.selected_pdo_num;
|
|
#else
|
|
obj.request_data_object.min_current = 10;
|
|
obj.request_data_object.op_current = 10;
|
|
obj.request_data_object.object_position = 1;
|
|
#endif
|
|
#endif
|
|
|
|
#if 0
|
|
if(obj.request_data_object.object_position == 0){
|
|
if(pd_data->protocol_rx.data_obj[0].power_data_obj.max_current >= (500/USBPD_CURRENT_UNIT)){
|
|
obj.request_data_object.min_current = 500 / USBPD_CURRENT_UNIT;
|
|
obj.request_data_object.op_current = 500 / USBPD_CURRENT_UNIT;
|
|
}
|
|
obj.request_data_object.object_position = 1;
|
|
}
|
|
|
|
dev_info(pd_data->dev, "Request [%d] FIXED min_current = %dmA, op_current = %dmA\n",
|
|
obj.request_data_object.object_position,
|
|
obj.request_data_object.min_current * USBPD_CURRENT_UNIT,
|
|
obj.request_data_object.op_current * USBPD_CURRENT_UNIT);
|
|
#endif
|
|
|
|
return obj;
|
|
}
|
|
|
|
/*
|
|
usbpd_manager_evaluate_capability
|
|
: Policy engine ask Device Policy Manager to evaluate option
|
|
based on supplied capabilities
|
|
return >0 : request object number
|
|
0 : no selected option
|
|
*/
|
|
int usbpd_manager_evaluate_capability(struct usbpd_data *pd_data)
|
|
{
|
|
struct policy_data *policy = &pd_data->policy;
|
|
int i = 0;
|
|
int power_type = 0;
|
|
int pd_volt = 0, pd_current;
|
|
#ifdef CONFIG_BATTERY_SAMSUNG
|
|
#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER
|
|
struct usbpd_manager_data *manager = &pd_data->manager;
|
|
int available_pdo_num = 0;
|
|
PDIC_SINK_STATUS *pdic_sink_status = &pd_noti.sink_status;
|
|
#endif
|
|
#endif
|
|
data_obj_type *pd_obj;
|
|
|
|
for (i = 0; i < policy->rx_msg_header.num_data_objs; i++) {
|
|
pd_obj = &policy->rx_data_obj[i];
|
|
power_type = pd_obj->power_data_obj_supply_type.supply_type;
|
|
switch (power_type) {
|
|
case POWER_TYPE_FIXED:
|
|
pd_volt = pd_obj->power_data_obj.voltage;
|
|
pd_current = pd_obj->power_data_obj.max_current;
|
|
dev_info(pd_data->dev, "[%d] FIXED volt(%d)mV, max current(%d)\n",
|
|
i+1, pd_volt * USBPD_VOLT_UNIT, pd_current * USBPD_CURRENT_UNIT);
|
|
#ifdef CONFIG_BATTERY_SAMSUNG
|
|
#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER
|
|
if (pd_volt * USBPD_VOLT_UNIT <= MAX_CHARGING_VOLT)
|
|
available_pdo_num = i + 1;
|
|
pdic_sink_status->power_list[i + 1].max_voltage = pd_volt * USBPD_VOLT_UNIT;
|
|
pdic_sink_status->power_list[i + 1].max_current = pd_current * USBPD_CURRENT_UNIT;
|
|
#endif
|
|
#endif
|
|
break;
|
|
case POWER_TYPE_BATTERY:
|
|
pd_volt = pd_obj->power_data_obj_battery.max_voltage;
|
|
dev_info(pd_data->dev, "[%d] BATTERY volt(%d)mV\n",
|
|
i+1, pd_volt * USBPD_VOLT_UNIT);
|
|
break;
|
|
case POWER_TYPE_VARIABLE:
|
|
pd_volt = pd_obj->power_data_obj_variable.max_voltage;
|
|
dev_info(pd_data->dev, "[%d] VARIABLE volt(%d)mV\n",
|
|
i+1, pd_volt * USBPD_VOLT_UNIT);
|
|
break;
|
|
case POWER_TYPE_PPS:
|
|
dev_info(pd_data->dev, "[%d] PPS (%d)mV-(%d)mV / %dmA\n",
|
|
i+1, pd_obj->power_data_obj_pps.min_voltage * USBPD_VOLT_UNIT,
|
|
pd_obj->power_data_obj_pps.max_voltage * USBPD_VOLT_UNIT,
|
|
pd_obj->power_data_obj_pps.max_current * USBPD_CURRENT_UNIT);
|
|
break;
|
|
default:
|
|
dev_err(pd_data->dev, "[%d] Power Type Error\n", i+1);
|
|
break;
|
|
}
|
|
}
|
|
#ifdef CONFIG_BATTERY_SAMSUNG
|
|
#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER
|
|
if (manager->flash_mode == 1)
|
|
available_pdo_num = 1;
|
|
if ((pdic_sink_status->available_pdo_num > 0) &&
|
|
(pdic_sink_status->available_pdo_num != available_pdo_num)) {
|
|
policy->send_sink_cap = 1;
|
|
pdic_sink_status->selected_pdo_num = 1;
|
|
}
|
|
pdic_sink_status->available_pdo_num = available_pdo_num;
|
|
return available_pdo_num;
|
|
#endif
|
|
#else
|
|
return 1; /* select default first obj */
|
|
#endif
|
|
}
|
|
|
|
/* return: 0: cab be met, -1: cannot be met, -2: could be met later */
|
|
int usbpd_manager_match_request(struct usbpd_data *pd_data)
|
|
{
|
|
/* TODO: Evaluation of sink request */
|
|
unsigned supply_type
|
|
= pd_data->source_request_obj.power_data_obj_supply_type.supply_type;
|
|
unsigned src_max_current, mismatch, max_min, op, pos;
|
|
|
|
if (supply_type == POWER_TYPE_FIXED)
|
|
pr_info("REQUEST: FIXED\n");
|
|
else if (supply_type == POWER_TYPE_VARIABLE)
|
|
pr_info("REQUEST: VARIABLE\n");
|
|
else if (supply_type == POWER_TYPE_PPS)
|
|
pr_info("REQUEST: PPS\n");
|
|
else if (supply_type == POWER_TYPE_BATTERY) {
|
|
pr_info("REQUEST: BATTERY\n");
|
|
goto log_battery;
|
|
}else {
|
|
pr_info("REQUEST: UNKNOWN Supply type.\n");
|
|
return -1;
|
|
}
|
|
|
|
/* Tx Source PDO */
|
|
src_max_current = pd_data->source_data_obj.power_data_obj.max_current;
|
|
|
|
/* Rx Request RDO */
|
|
mismatch = pd_data->source_request_obj.request_data_object.capability_mismatch;
|
|
max_min = pd_data->source_request_obj.request_data_object.min_current;
|
|
op = pd_data->source_request_obj.request_data_object.op_current;
|
|
pos = pd_data->source_request_obj.request_data_object.object_position;
|
|
|
|
/*src_max_current is already *10 value ex) src_max_current 500mA */
|
|
pr_info("Tx SourceCap Current : %dmA\n", src_max_current*10);
|
|
pr_info("Rx Request Current : %dmA\n", max_min*10);
|
|
|
|
/* Compare Pdo and Rdo */
|
|
if ((src_max_current >= op) && (pos == 1))
|
|
return 0;
|
|
else
|
|
return -1;
|
|
|
|
log_battery:
|
|
mismatch = pd_data->source_request_obj.request_data_object_battery.capability_mismatch;
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_OF
|
|
static int of_usbpd_manager_dt(struct usbpd_manager_data *_data)
|
|
{
|
|
int ret = 0;
|
|
struct device_node *np =
|
|
of_find_node_by_name(NULL, "pdic-manager");
|
|
|
|
if (np == NULL) {
|
|
pr_err("%s np NULL\n", __func__);
|
|
return -EINVAL;
|
|
} else {
|
|
ret = of_property_read_u32(np, "pdic,max_power",
|
|
&_data->max_power);
|
|
if (ret < 0)
|
|
pr_err("%s error reading max_power %d\n",
|
|
__func__, _data->max_power);
|
|
|
|
ret = of_property_read_u32(np, "pdic,op_power",
|
|
&_data->op_power);
|
|
if (ret < 0)
|
|
pr_err("%s error reading op_power %d\n",
|
|
__func__, _data->max_power);
|
|
|
|
ret = of_property_read_u32(np, "pdic,max_current",
|
|
&_data->max_current);
|
|
if (ret < 0)
|
|
pr_err("%s error reading max_current %d\n",
|
|
__func__, _data->max_current);
|
|
|
|
ret = of_property_read_u32(np, "pdic,min_current",
|
|
&_data->min_current);
|
|
if (ret < 0)
|
|
pr_err("%s error reading min_current %d\n",
|
|
__func__, _data->min_current);
|
|
|
|
_data->giveback = of_property_read_bool(np,
|
|
"pdic,giveback");
|
|
_data->usb_com_capable = of_property_read_bool(np,
|
|
"pdic,usb_com_capable");
|
|
_data->no_usb_suspend = of_property_read_bool(np,
|
|
"pdic,no_usb_suspend");
|
|
|
|
/* source capability */
|
|
ret = of_property_read_u32(np, "source,max_voltage",
|
|
&_data->source_max_volt);
|
|
ret = of_property_read_u32(np, "source,min_voltage",
|
|
&_data->source_min_volt);
|
|
ret = of_property_read_u32(np, "source,max_power",
|
|
&_data->source_max_power);
|
|
|
|
/* sink capability */
|
|
ret = of_property_read_u32(np, "sink,capable_max_voltage",
|
|
&_data->sink_cap_max_volt);
|
|
if (ret < 0) {
|
|
_data->sink_cap_max_volt = 5000;
|
|
pr_err("%s error reading sink_cap_max_volt %d\n",
|
|
__func__, _data->sink_cap_max_volt);
|
|
}
|
|
|
|
ret = of_property_read_u32(np, "pdic,vb_cc_short_max_input_current",
|
|
&_data->vb_cc_short_max_input_current);
|
|
if (ret < 0) {
|
|
_data->vb_cc_short_max_input_current = 1800;
|
|
pr_err("%s error reading vb_cc_short_max_input_current %d\n",
|
|
__func__, _data->vb_cc_short_max_input_current);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
void usbpd_init_manager_val(struct usbpd_data *pd_data)
|
|
{
|
|
struct usbpd_manager_data *manager = &pd_data->manager;
|
|
|
|
pr_info("%s\n", __func__);
|
|
manager->alt_sended = 0;
|
|
manager->cmd = 0;
|
|
manager->vdm_en = 0;
|
|
manager->Vendor_ID = 0;
|
|
manager->Product_ID = 0;
|
|
manager->Device_Version = 0;
|
|
manager->SVID_0 = 0;
|
|
manager->SVID_1 = 0;
|
|
manager->Standard_Vendor_ID = 0;
|
|
manager->prev_available_pdo = 0;
|
|
manager->ps_rdy = 0;
|
|
reinit_completion(&manager->uvdm_out_wait);
|
|
reinit_completion(&manager->uvdm_in_wait);
|
|
usbpd_manager_select_pdo_cancel(pd_data->dev);
|
|
usbpd_manager_start_discover_msg_cancel(pd_data->dev);
|
|
}
|
|
|
|
int usbpd_init_manager(struct usbpd_data *pd_data)
|
|
{
|
|
int ret = 0;
|
|
struct usbpd_manager_data *manager = &pd_data->manager;
|
|
|
|
pr_info("%s\n", __func__);
|
|
if (manager == NULL) {
|
|
pr_err("%s, usbpd manager data is error!!\n", __func__);
|
|
return -ENOMEM;
|
|
} else
|
|
ret = of_usbpd_manager_dt(manager);
|
|
#ifdef CONFIG_BATTERY_SAMSUNG
|
|
#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER
|
|
fp_select_pdo = usbpd_manager_select_pdo;
|
|
#endif
|
|
#endif
|
|
mutex_init(&manager->vdm_mutex);
|
|
mutex_init(&manager->pdo_mutex);
|
|
manager->pd_data = pd_data;
|
|
manager->power_role_swap = true;
|
|
manager->data_role_swap = true;
|
|
manager->vconn_source_swap = 0;
|
|
manager->alt_sended = 0;
|
|
manager->vdm_en = 0;
|
|
manager->acc_type = 0;
|
|
manager->Vendor_ID = 0;
|
|
manager->Product_ID = 0;
|
|
manager->Device_Version = 0;
|
|
manager->SVID_0 = 0;
|
|
manager->SVID_1 = 0;
|
|
manager->Standard_Vendor_ID = 0;
|
|
|
|
manager->flash_mode = 0;
|
|
manager->prev_available_pdo = 0;
|
|
manager->ps_rdy = 0;
|
|
|
|
init_completion(&manager->uvdm_out_wait);
|
|
init_completion(&manager->uvdm_in_wait);
|
|
|
|
usbpd_manager_register_switch_device(1);
|
|
init_source_cap_data(manager);
|
|
init_sink_cap_data(manager);
|
|
INIT_DELAYED_WORK(&manager->acc_detach_handler, usbpd_manager_acc_detach_handler);
|
|
INIT_DELAYED_WORK(&manager->select_pdo_handler, usbpd_manager_select_pdo_handler);
|
|
INIT_DELAYED_WORK(&manager->start_discover_msg_handler,
|
|
usbpd_manager_start_discover_msg_handler);
|
|
|
|
ret = ccic_misc_init();
|
|
if (ret) {
|
|
pr_info("ccic misc register is failed, error %d\n", ret);
|
|
|
|
}
|
|
pr_info("%s done\n", __func__);
|
|
return ret;
|
|
}
|