blob: 373a21ac9df6587b5cddc7ec4d3338ba2862dbf6 [file] [log] [blame]
/*
driver/usbpd/s2mu004.c - S2MU004 USB PD(Power Delivery) device driver
*
* Copyright (C) 2016 Samsung Electronics
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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 the
* GNU General Public License for more details.
*
*/
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/completion.h>
#include <linux/ccic/usbpd.h>
#include <linux/ccic/usbpd-s2mu004.h>
#include <linux/mfd/samsung/s2mu004.h>
#include <linux/mfd/samsung/s2mu004-private.h>
#include <linux/muic/muic.h>
#if defined(CONFIG_MUIC_NOTIFIER)
#include <linux/muic/muic_notifier.h>
#endif /* CONFIG_MUIC_NOTIFIER */
#include <linux/sec_batt.h>
#ifdef CONFIG_BATTERY_SAMSUNG
#include <linux/battery/sec_charging_common.h>
#else
#include <linux/power/s2mu004_charger_common.h>
#include <power_supply.h>
#endif
#if defined(CONFIG_USB_HOST_NOTIFY) || defined(CONFIG_USB_HW_PARAM)
#include <linux/usb_notify.h>
#endif
#include <linux/regulator/consumer.h>
#include <linux/ccic/usbpd_ext.h>
#ifdef CONFIG_BATTERY_SAMSUNG
#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER
struct pdic_notifier_struct pd_noti;
#endif
#endif
/*
*VARIABLE DEFINITION
*/
static usbpd_phy_ops_type s2mu004_ops;
struct i2c_client *test_i2c;
/*
*FUNCTION DEFINITION
*/
static int s2mu004_receive_message(void *data);
static int s2mu004_check_port_detect(struct s2mu004_usbpd_data *pdic_data);
static int s2mu004_usbpd_reg_init(struct s2mu004_usbpd_data *_data);
static void s2mu004_dfp(struct i2c_client *i2c);
static void s2mu004_ufp(struct i2c_client *i2c);
static void s2mu004_usbpd_check_msg(void *_data, u64 *val);
#ifdef CONFIG_CCIC_VDM
static int s2mu004_usbpd_check_vdm_msg(void *_data, u64 *val);
#endif
static void s2mu004_src(struct i2c_client *i2c);
static void s2mu004_snk(struct i2c_client *i2c);
static void s2mu004_assert_rd(void *_data);
static void s2mu004_assert_rp(void *_data);
#if (defined(CONFIG_DUAL_ROLE_USB_INTF) || defined(CONFIG_TYPEC))
static int s2mu004_set_attach(struct s2mu004_usbpd_data *pdic_data, u8 mode);
static int s2mu004_set_detach(struct s2mu004_usbpd_data *pdic_data, u8 mode);
#endif
static void s2mu004_usbpd_check_rid(struct s2mu004_usbpd_data *pdic_data);
static int s2mu004_usbpd_read_reg(struct i2c_client *i2c, u8 reg, u8 *dest);
static int s2mu004_usbpd_write_reg(struct i2c_client *i2c, u8 reg, u8 value);
#ifndef CONFIG_SEC_FACTORY
static void s2mu004_usbpd_set_threshold(struct s2mu004_usbpd_data *pdic_data,
CCIC_RP_RD_SEL port_sel, CCIC_THRESHOLD_SEL threshold_sel);
static void s2mu004_usbpd_notify_detach(struct s2mu004_usbpd_data *pdic_data);
#endif
static void s2mu004_usbpd_detach_init(struct s2mu004_usbpd_data *pdic_data);
static int s2mu004_usbpd_set_cc_control(struct s2mu004_usbpd_data *pdic_data, int val);
char *rid_text[] = {
"UNDEFINED",
"RID ERROR",
"RID ERROR",
"RID 255K",
"RID 301K",
"RID 523K",
"RID 619K"
};
#if defined(CONFIG_CCIC_NOTIFIER)
extern struct device *ccic_device;
#endif
#if (defined(CONFIG_DUAL_ROLE_USB_INTF) || defined(CONFIG_TYPEC))
void s2mu004_rprd_mode_change(struct s2mu004_usbpd_data *usbpd_data, u8 mode)
{
u8 data = 0;
struct i2c_client *i2c = usbpd_data->i2c;
pr_info("%s, mode=0x%x\n", __func__, mode);
mutex_lock(&usbpd_data->lpm_mutex);
if (usbpd_data->lpm_mode)
goto skip;
switch (mode) {
case TYPE_C_ATTACH_DFP: /* SRC */
s2mu004_set_detach(usbpd_data, mode);
msleep(S2MU004_ROLE_SWAP_TIME_MS);
s2mu004_set_attach(usbpd_data, mode);
break;
case TYPE_C_ATTACH_UFP: /* SNK */
s2mu004_set_detach(usbpd_data, mode);
msleep(S2MU004_ROLE_SWAP_TIME_MS);
s2mu004_set_attach(usbpd_data, mode);
break;
case TYPE_C_ATTACH_DRP: /* DRP */
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_CTRL_PORT, &data);
data |= S2MU004_REG_PLUG_CTRL_DRP;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_PORT, data);
break;
};
skip:
mutex_unlock(&usbpd_data->lpm_mutex);
}
#endif
void vbus_turn_on_ctrl(struct s2mu004_usbpd_data *usbpd_data, bool enable)
{
struct power_supply *psy_otg;
union power_supply_propval val;
int on = !!enable;
int ret = 0, retry_cnt = 0;
pr_info("%s %d, enable=%d\n", __func__, __LINE__, enable);
psy_otg = get_power_supply_by_name("otg");
if (psy_otg) {
val.intval = enable;
usbpd_data->is_otg_vboost = enable;
ret = psy_otg->desc->set_property(psy_otg, POWER_SUPPLY_PROP_ONLINE, &val);
} else {
pr_err("%s: Fail to get psy battery\n", __func__);
return;
}
if (ret) {
pr_err("%s: fail to set power_suppy ONLINE property(%d)\n",
__func__, ret);
} else {
if (enable == VBUS_ON) {
for (retry_cnt = 0; retry_cnt < 5; retry_cnt++) {
psy_otg->desc->get_property(psy_otg, POWER_SUPPLY_PROP_ONLINE, &val);
if (val.intval == VBUS_OFF) {
msleep(100);
val.intval = enable;
psy_otg->desc->set_property(psy_otg, POWER_SUPPLY_PROP_ONLINE, &val);
} else
break;
}
}
pr_info("otg accessory power = %d\n", on);
}
}
#if defined(CONFIG_CCIC_NOTIFIER)
static void process_dr_swap(struct s2mu004_usbpd_data *usbpd_data)
{
struct i2c_client *i2c = usbpd_data->i2c;
dev_info(&i2c->dev, "%s : before - is_host : %d, is_client : %d\n",
__func__, usbpd_data->is_host, usbpd_data->is_client);
if (usbpd_data->is_host == HOST_ON) {
ccic_event_work(usbpd_data,
CCIC_NOTIFY_DEV_USB, CCIC_NOTIFY_ID_USB,
0/*attach*/, USB_STATUS_NOTIFY_DETACH/*drp*/);
ccic_event_work(usbpd_data,
CCIC_NOTIFY_DEV_USB, CCIC_NOTIFY_ID_USB,
1/*attach*/, USB_STATUS_NOTIFY_ATTACH_UFP/*drp*/);
usbpd_data->is_host = HOST_OFF;
usbpd_data->is_client = CLIENT_ON;
} else if (usbpd_data->is_client == CLIENT_ON) {
ccic_event_work(usbpd_data,
CCIC_NOTIFY_DEV_USB, CCIC_NOTIFY_ID_USB,
0/*attach*/, USB_STATUS_NOTIFY_DETACH/*drp*/);
ccic_event_work(usbpd_data,
CCIC_NOTIFY_DEV_USB, CCIC_NOTIFY_ID_USB,
1/*attach*/, USB_STATUS_NOTIFY_ATTACH_DFP/*drp*/);
usbpd_data->is_host = HOST_ON;
usbpd_data->is_client = CLIENT_OFF;
}
dev_info(&i2c->dev, "%s : after - is_host : %d, is_client : %d\n",
__func__, usbpd_data->is_host, usbpd_data->is_client);
}
#endif
static void s2mu004_pr_swap(void *_data, int val)
{
struct usbpd_data *data = (struct usbpd_data *) _data;
struct s2mu004_usbpd_data *pdic_data = data->phy_driver_data;
if (val == USBPD_SINK_OFF) {
pd_noti.event = PDIC_NOTIFY_EVENT_PD_PRSWAP_SNKTOSRC;
pd_noti.sink_status.selected_pdo_num = 0;
pd_noti.sink_status.available_pdo_num = 0;
pd_noti.sink_status.current_pdo_num = 0;
ccic_event_work(pdic_data, CCIC_NOTIFY_DEV_BATTERY,
CCIC_NOTIFY_ID_POWER_STATUS, 0, 0);
} else if (val == USBPD_SOURCE_ON) {
#if defined(CONFIG_DUAL_ROLE_USB_INTF)
pdic_data->power_role_dual = DUAL_ROLE_PROP_PR_SRC;
#elif defined(CONFIG_TYPEC)
pdic_data->typec_power_role = TYPEC_SOURCE;
typec_set_pwr_role(pdic_data->port, pdic_data->typec_power_role);
#endif
ccic_event_work(pdic_data, CCIC_NOTIFY_DEV_MUIC,
CCIC_NOTIFY_ID_ROLE_SWAP, 1/* source */, 0);
} else if (val == USBPD_SOURCE_OFF) {
#if defined(CONFIG_DUAL_ROLE_USB_INTF)
pdic_data->power_role_dual = DUAL_ROLE_PROP_PR_SNK;
#elif defined(CONFIG_TYPEC)
pdic_data->typec_power_role = TYPEC_SINK;
typec_set_pwr_role(pdic_data->port, pdic_data->typec_power_role);
#endif
ccic_event_work(pdic_data, CCIC_NOTIFY_DEV_MUIC,
CCIC_NOTIFY_ID_ROLE_SWAP, 0/* sink */, 0);
}
}
static int s2mu004_usbpd_read_reg(struct i2c_client *i2c, u8 reg, u8 *dest)
{
int ret;
struct device *dev = &i2c->dev;
#if defined(CONFIG_USB_HW_PARAM)
struct otg_notify *o_notify = get_otg_notify();
#endif
ret = i2c_smbus_read_byte_data(i2c, reg);
if (ret < 0) {
dev_err(dev, "%s reg(0x%x), ret(%d)\n", __func__, reg, ret);
#if defined(CONFIG_USB_HW_PARAM)
if (o_notify)
inc_hw_param(o_notify, USB_CCIC_I2C_ERROR_COUNT);
#endif
return ret;
}
ret &= 0xff;
*dest = ret;
return 0;
}
static int s2mu004_usbpd_bulk_read(struct i2c_client *i2c, u8 reg, int count, u8 *buf)
{
int ret;
struct device *dev = &i2c->dev;
#if defined(CONFIG_USB_HW_PARAM)
struct otg_notify *o_notify = get_otg_notify();
#endif
#ifdef CONFIG_SEC_FACTORY
int retry = 0;
#endif
ret = i2c_smbus_read_i2c_block_data(i2c, reg, count, buf);
#ifdef CONFIG_SEC_FACTORY
for (retry = 0; retry < 5; retry++) {
if (ret < 0) {
dev_err(dev, "%s reg(0x%x), ret(%d) retry(%d) after now\n",
__func__, reg, ret, retry);
msleep(40);
ret = i2c_smbus_read_i2c_block_data(i2c, reg, count, buf);
} else
break;
}
if (ret < 0) {
dev_err(dev, "%s failed to read reg, ret(%d)\n", __func__, ret);
#else
if (ret < 0) {
dev_err(dev, "%s reg(0x%x), ret(%d)\n", __func__, reg, ret);
#endif
#if defined(CONFIG_USB_HW_PARAM)
if (o_notify)
inc_hw_param(o_notify, USB_CCIC_I2C_ERROR_COUNT);
#endif
return ret;
}
return 0;
}
static int s2mu004_usbpd_write_reg(struct i2c_client *i2c, u8 reg, u8 value)
{
int ret;
struct device *dev = &i2c->dev;
#if defined(CONFIG_USB_HW_PARAM)
struct otg_notify *o_notify = get_otg_notify();
#endif
if (reg == S2MU004_REG_PLUG_CTRL_SET_RD)
value |= 0x40;
ret = i2c_smbus_write_byte_data(i2c, reg, value);
if (ret < 0) {
dev_err(dev, "%s reg(0x%x), ret(%d)\n", __func__, reg, ret);
#if defined(CONFIG_USB_HW_PARAM)
if (o_notify)
inc_hw_param(o_notify, USB_CCIC_I2C_ERROR_COUNT);
#endif
}
return ret;
}
static int s2mu004_usbpd_bulk_write(struct i2c_client *i2c, u8 reg, int count, u8 *buf)
{
int ret;
struct device *dev = &i2c->dev;
#if defined(CONFIG_USB_HW_PARAM)
struct otg_notify *o_notify = get_otg_notify();
#endif
ret = i2c_smbus_write_i2c_block_data(i2c, reg, count, buf);
if (ret < 0) {
dev_err(dev, "%s reg(0x%x), ret(%d)\n", __func__, reg, ret);
#if defined(CONFIG_USB_HW_PARAM)
if (o_notify)
inc_hw_param(o_notify, USB_CCIC_I2C_ERROR_COUNT);
#endif
return ret;
}
return 0;
}
static int s2mu004_usbpd_update_bit(struct i2c_client *i2c,
u8 reg, u8 mask, u8 shift, u8 value)
{
int ret;
u8 reg_val = 0;
ret = s2mu004_usbpd_read_reg(i2c, reg, &reg_val);
if (ret < 0) {
pr_err("%s: Reg = 0x%X, val = 0x%X, read err : %d\n",
__func__, reg, reg_val, ret);
}
reg_val &= ~mask;
reg_val |= value << shift;
ret = s2mu004_usbpd_write_reg(i2c, reg, reg_val);
if (ret < 0) {
pr_err("%s: Reg = 0x%X, mask = 0x%X, val = 0x%X, write err : %d\n",
__func__, reg, mask, value, ret);
}
return ret;
}
static int s2mu004_write_msg_header(struct i2c_client *i2c, u8 *buf)
{
int ret;
ret = s2mu004_usbpd_bulk_write(i2c, S2MU004_REG_MSG_HEADER_L, 2, buf);
return ret;
}
static int s2mu004_write_msg_obj(struct i2c_client *i2c, int count, data_obj_type *obj)
{
int ret = 0;
int i = 0;
struct device *dev = &i2c->dev;
if (count > S2MU004_MAX_NUM_MSG_OBJ)
dev_err(dev, "%s, not invalid obj count number\n", __func__);
else
for (i = 0; i < count; i++) {
ret = s2mu004_usbpd_bulk_write(i2c,
S2MU004_REG_MSG_OBJECT0_0_L + (4 * i),
4, obj[i].byte);
}
return ret;
}
static int s2mu004_send_msg(struct i2c_client *i2c)
{
int ret;
u8 reg = S2MU004_REG_MSG_SEND_CON;
u8 val = S2MU004_REG_MSG_SEND_CON_OP_MODE
| S2MU004_REG_MSG_SEND_CON_SEND_MSG_EN;
s2mu004_usbpd_write_reg(i2c, reg, val);
ret = s2mu004_usbpd_write_reg(i2c, reg, S2MU004_REG_MSG_SEND_CON_OP_MODE);
return ret;
}
static int s2mu004_read_msg_header(struct i2c_client *i2c, msg_header_type *header)
{
int ret;
ret = s2mu004_usbpd_bulk_read(i2c, S2MU004_REG_MSG_HEADER_L, 2, header->byte);
return ret;
}
static int s2mu004_read_msg_obj(struct i2c_client *i2c, int count, data_obj_type *obj)
{
int ret = 0;
int i = 0;
struct device *dev = &i2c->dev;
if (count > S2MU004_MAX_NUM_MSG_OBJ) {
dev_err(dev, "%s, not invalid obj count number\n", __func__);
ret = -EINVAL; /*TODO: check fail case */
} else {
for (i = 0; i < count; i++) {
ret = s2mu004_usbpd_bulk_read(i2c,
S2MU004_REG_MSG_OBJECT0_0_L + (4 * i),
4, obj[i].byte);
}
}
return ret;
}
static void s2mu004_set_irq_enable(struct s2mu004_usbpd_data *_data,
u8 int0, u8 int1, u8 int2, u8 int3, u8 int4, u8 int5)
{
u8 int_mask[S2MU004_MAX_NUM_INT_STATUS]
= {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
int ret = 0;
struct i2c_client *i2c = _data->i2c;
struct device *dev = &i2c->dev;
int_mask[0] &= ~int0;
int_mask[1] &= ~int1;
int_mask[2] &= ~int2;
int_mask[3] &= ~int3;
int_mask[4] &= ~int4;
int_mask[5] &= ~int5;
ret = i2c_smbus_write_i2c_block_data(i2c, S2MU004_REG_INT_MASK0,
S2MU004_MAX_NUM_INT_STATUS, int_mask);
if (ret < 0)
dev_err(dev, "err write interrupt mask \n");
}
static void s2mu004_self_soft_reset(struct i2c_client *i2c)
{
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_ETC,
S2MU004_REG_ETC_SOFT_RESET_EN);
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_ETC,
S2MU004_REG_ETC_SOFT_RESET_DIS);
}
static void s2mu004_driver_reset(void *_data)
{
struct usbpd_data *data = (struct usbpd_data *) _data;
struct s2mu004_usbpd_data *pdic_data = data->phy_driver_data;
int i;
pdic_data->status_reg = 0;
data->wait_for_msg_arrived = 0;
pdic_data->header.word = 0;
for (i = 0; i < S2MU004_MAX_NUM_MSG_OBJ; i++)
pdic_data->obj[i].object = 0;
s2mu004_set_irq_enable(pdic_data, ENABLED_INT_0, ENABLED_INT_1,
ENABLED_INT_2, ENABLED_INT_3, ENABLED_INT_4, ENABLED_INT_5);
}
static void s2mu004_assert_drp(void *_data)
{
struct usbpd_data *data = (struct usbpd_data *) _data;
struct s2mu004_usbpd_data *pdic_data = data->phy_driver_data;
struct i2c_client *i2c = pdic_data->i2c;
u8 val;
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_CTRL_RpRd, &val);
val &= ~S2MU004_REG_PLUG_CTRL_FSM_MANUAL_EN;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_RpRd, val);
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_CTRL_CC12, &val);
val &= ~S2MU004_REG_PLUG_CTRL_FSM_MANUAL_INPUT_MASK;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_CC12, val);
}
static void s2mu004_assert_rd(void *_data)
{
struct usbpd_data *data = (struct usbpd_data *) _data;
struct s2mu004_usbpd_data *pdic_data = data->phy_driver_data;
struct i2c_client *i2c = pdic_data->i2c;
u8 val;
#if 0
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_MON1, &val);
cc1_val = val & S2MU004_REG_CTRL_MON_CC1_MASK;
cc2_val = (val & S2MU004_REG_CTRL_MON_CC2_MASK) >> S2MU004_REG_CTRL_MON_CC2_SHIFT;
#endif
if (pdic_data->cc1_val == 2) {
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_CTRL_CC12, &val);
val = (val & ~S2MU004_REG_PLUG_CTRL_CC_MANUAL_MASK) |
S2MU004_REG_PLUG_CTRL_CC1_MANUAL_ON;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_CC12, val);
if (pdic_data->vconn_en) {
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_CTRL_RpRd, &val);
val = (val & ~S2MU004_REG_PLUG_CTRL_CC_MANUAL_MASK) |
S2MU004_REG_PLUG_CTRL_RpRd_CC2_VCONN |
S2MU004_REG_PLUG_CTRL_VCONN_MANUAL_EN;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_RpRd, val);
}
}
if (pdic_data->cc2_val == 2) {
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_CTRL_CC12, &val);
val = (val & ~S2MU004_REG_PLUG_CTRL_CC_MANUAL_MASK) |
S2MU004_REG_PLUG_CTRL_CC2_MANUAL_ON;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_CC12, val);
if (pdic_data->vconn_en) {
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_CTRL_RpRd, &val);
val = (val & ~S2MU004_REG_PLUG_CTRL_CC_MANUAL_MASK) |
S2MU004_REG_PLUG_CTRL_RpRd_CC1_VCONN |
S2MU004_REG_PLUG_CTRL_VCONN_MANUAL_EN;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_RpRd, val);
}
}
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_CTRL_CC12, &val);
val &= ~S2MU004_REG_PLUG_CTRL_FSM_MANUAL_INPUT_MASK;
val |= S2MU004_REG_PLUG_CTRL_FSM_ATTACHED_SNK;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_CC12, val);
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_CTRL_RpRd, &val);
val |= S2MU004_REG_PLUG_CTRL_FSM_MANUAL_EN;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_RpRd, val);
}
static void s2mu004_assert_rp(void *_data)
{
struct usbpd_data *data = (struct usbpd_data *) _data;
struct s2mu004_usbpd_data *pdic_data = data->phy_driver_data;
struct i2c_client *i2c = pdic_data->i2c;
u8 val;
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_CTRL_CC12, &val);
val &= ~S2MU004_REG_PLUG_CTRL_FSM_MANUAL_INPUT_MASK;
val |= S2MU004_REG_PLUG_CTRL_FSM_ATTACHED_SRC;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_CC12, val);
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_CTRL_RpRd, &val);
val |= S2MU004_REG_PLUG_CTRL_FSM_MANUAL_EN;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_RpRd, val);
}
static unsigned s2mu004_get_status(void *_data, u64 flag)
{
struct usbpd_data *data = (struct usbpd_data *) _data;
struct s2mu004_usbpd_data *pdic_data = data->phy_driver_data;
u64 one = 1;
if (pdic_data->status_reg & (one << flag)) {
pdic_data->status_reg &= ~(one << flag); /* clear the flag */
return 1;
} else {
return 0;
}
}
static bool s2mu004_poll_status(void *_data)
{
struct usbpd_data *data = (struct usbpd_data *) _data;
struct policy_data *policy = &data->policy;
struct s2mu004_usbpd_data *pdic_data = data->phy_driver_data;
struct i2c_client *i2c = pdic_data->i2c;
struct device *dev = &i2c->dev;
u8 intr[S2MU004_MAX_NUM_INT_STATUS] = {0};
int ret = 0, retry = 0;
u64 status_reg_val = 0;
ret = s2mu004_usbpd_bulk_read(i2c, S2MU004_REG_INT_STATUS0,
S2MU004_MAX_NUM_INT_STATUS, intr);
dev_info(dev, "%s status[0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x]\n",
__func__, intr[0], intr[1], intr[2], intr[3], intr[4], intr[5], intr[6]);
if ((intr[0] | intr[1] | intr[2] | intr[3] | intr[4] | intr[5]) == 0)
goto out;
if (intr[5] & S2MU004_REG_INT_STATUS5_HARD_RESET) {
status_reg_val |= 1 << MSG_HARDRESET;
goto out;
}
if ((intr[5] & S2MU004_REG_INT_STATUS5_SOP_PRIME) &&
(intr[5] & S2MU004_REG_INT_STATUS5_SOP) == 0)
s2mu004_self_soft_reset(i2c);
/* when occur detach & attach atomic */
if (intr[4] & S2MU004_REG_INT_STATUS4_USB_DETACH) {
status_reg_val |= 1 << PLUG_DETACH;
}
mutex_lock(&pdic_data->lpm_mutex);
if ((intr[4] & S2MU004_REG_INT_STATUS4_PLUG_IRQ) &&
!pdic_data->lpm_mode && !pdic_data->is_water_detect)
status_reg_val |= 1 << PLUG_ATTACH;
else if (pdic_data->lpm_mode &&
(intr[4] & S2MU004_REG_INT_STATUS4_PLUG_IRQ) &&
!pdic_data->is_water_detect)
retry = 1;
mutex_unlock(&pdic_data->lpm_mutex);
if (retry) {
msleep(40);
mutex_lock(&pdic_data->lpm_mutex);
if ((intr[4] & S2MU004_REG_INT_STATUS4_PLUG_IRQ) &&
!pdic_data->lpm_mode && !pdic_data->is_water_detect)
status_reg_val |= 1 << PLUG_ATTACH;
mutex_unlock(&pdic_data->lpm_mutex);
}
/* function that support dp control */
if (intr[4] & S2MU004_REG_INT_STATUS4_MSG_PASS)
status_reg_val |= 1 << MSG_PASS;
/* #if defined(CONFIG_CCIC_FACTORY) */
if (intr[3] & S2MU004_REG_INT_STATUS3_UNS_CMD_DATA) {
if (pdic_data->detach_valid)
status_reg_val |= 1 << PLUG_ATTACH;
status_reg_val |= 1 << MSG_RID;
}
/* #endif */
if (intr[0] & S2MU004_REG_INT_STATUS0_MSG_GOODCRC
|| intr[4] & S2MU004_REG_INT_STATUS4_MSG_SENT)
status_reg_val |= 1 << MSG_GOODCRC;
if (intr[0] & S2MU004_REG_INT_STATUS0_MSG_ACCEPT)
status_reg_val |= 1 << MSG_ACCEPT;
if (intr[1] & S2MU004_REG_INT_STATUS1_MSG_PSRDY)
status_reg_val |= 1 << MSG_PSRDY;
if (intr[2] & S2MU004_REG_INT_STATUS2_MSG_REQUEST)
status_reg_val |= 1 << MSG_REQUEST;
if (intr[1] & S2MU004_REG_INT_STATUS1_MSG_REJECT)
status_reg_val |= 1 << MSG_REJECT;
if (intr[2] & S2MU004_REG_INT_STATUS2_MSG_WAIT)
status_reg_val |= 1 << MSG_WAIT;
if (intr[4] & S2MU004_REG_INT_STATUS4_MSG_ERROR)
status_reg_val |= 1 << MSG_ERROR;
if (intr[1] & S2MU004_REG_INT_STATUS1_MSG_PING)
status_reg_val |= 1 << MSG_PING;
if (intr[1] & S2MU004_REG_INT_STATUS1_MSG_GETSNKCAP)
status_reg_val |= 1 << MSG_GET_SNK_CAP;
if (intr[1] & S2MU004_REG_INT_STATUS1_MSG_GETSRCCAP)
status_reg_val |= 1 << MSG_GET_SRC_CAP;
if (intr[2] & S2MU004_REG_INT_STATUS2_MSG_SRC_CAP) {
if (!policy->plug_valid)
pdic_data->status_reg |= 1 << PLUG_ATTACH;
}
if (intr[2] & S2MU004_REG_INT_STATUS2_MSG_SOFTRESET)
status_reg_val |= 1 << MSG_SOFTRESET;
if (intr[1] & S2MU004_REG_INT_STATUS1_MSG_PR_SWAP)
status_reg_val |= 1 << MSG_PR_SWAP;
if (intr[2] & S2MU004_REG_INT_STATUS2_MSG_VCONN_SWAP)
status_reg_val |= 1 << MSG_VCONN_SWAP;
if (intr[1] & S2MU004_REG_INT_STATUS1_MSG_DR_SWAP)
status_reg_val |= 1 << MSG_DR_SWAP;
/* read message if data object message */
if (status_reg_val &
((1 << MSG_REQUEST) | (1 << MSG_SNK_CAP) | (1 << MSG_SRC_CAP)
| (1 << VDM_DISCOVER_IDENTITY) | (1 << VDM_DISCOVER_SVID)
| (1 << VDM_DISCOVER_MODE) | (1 << VDM_ENTER_MODE)
| (1 << VDM_EXIT_MODE) | (1 << VDM_ATTENTION) | (1 << MSG_PASS))) {
usbpd_protocol_rx(data);
s2mu004_usbpd_check_msg(data, &status_reg_val);
#ifdef CONFIG_CCIC_VDM
if (status_reg_val & (1 << MSG_PASS))
s2mu004_usbpd_check_vdm_msg(data, &status_reg_val);
#endif
}
if ((intr[5] & S2MU004_REG_INT_STATUS5_SOP_PRIME) &&
(intr[5] & S2MU004_REG_INT_STATUS5_SOP) == 0)
usbpd_init_protocol(data);
out:
pdic_data->status_reg |= status_reg_val;
return 0;
}
static void s2mu004_soft_reset(void *_data)
{
struct usbpd_data *data = (struct usbpd_data *) _data;
struct s2mu004_usbpd_data *pdic_data = data->phy_driver_data;
struct i2c_client *i2c = pdic_data->i2c;
s2mu004_self_soft_reset(i2c);
}
static int s2mu004_hard_reset(void *_data)
{
struct usbpd_data *data = (struct usbpd_data *) _data;
struct s2mu004_usbpd_data *pdic_data = data->phy_driver_data;
struct i2c_client *i2c = pdic_data->i2c;
int ret;
u8 reg;
if (pdic_data->rid != REG_RID_UNDF && pdic_data->rid != REG_RID_MAX)
return 0;
reg = S2MU004_REG_MSG_SEND_CON;
ret = s2mu004_usbpd_write_reg(i2c, reg, S2MU004_REG_MSG_SEND_CON_SOP_HardRST
| S2MU004_REG_MSG_SEND_CON_OP_MODE);
if (ret < 0)
goto fail;
udelay(5);
ret = s2mu004_usbpd_write_reg(i2c, reg, S2MU004_REG_MSG_SEND_CON_SOP_HardRST
| S2MU004_REG_MSG_SEND_CON_OP_MODE
| S2MU004_REG_MSG_SEND_CON_SEND_MSG_EN);
if (ret < 0)
goto fail;
udelay(1);
ret = s2mu004_usbpd_write_reg(i2c, reg, S2MU004_REG_MSG_SEND_CON_OP_MODE);
udelay(1);
ret = s2mu004_usbpd_write_reg(i2c, reg, S2MU004_RESET_REG_00);
if (ret < 0)
goto fail;
s2mu004_self_soft_reset(i2c);
pdic_data->status_reg = 0;
if (pdic_data->power_role == PDIC_SOURCE)
s2mu004_dfp(i2c);
else
s2mu004_ufp(i2c);
return 0;
fail:
return -EIO;
}
static int s2mu004_receive_message(void *data)
{
struct s2mu004_usbpd_data *pdic_data = data;
struct i2c_client *i2c = pdic_data->i2c;
struct device *dev = &i2c->dev;
int obj_num = 0;
int ret = 0;
ret = s2mu004_read_msg_header(i2c, &pdic_data->header);
if (ret < 0)
dev_err(dev, "%s read msg header error\n", __func__);
obj_num = pdic_data->header.num_data_objs;
if (obj_num > 0) {
ret = s2mu004_read_msg_obj(i2c,
obj_num, &pdic_data->obj[0]);
}
return ret;
}
static int s2mu004_tx_msg(void *_data,
msg_header_type *header, data_obj_type *obj)
{
struct usbpd_data *data = (struct usbpd_data *) _data;
struct s2mu004_usbpd_data *pdic_data = data->phy_driver_data;
struct i2c_client *i2c = pdic_data->i2c;
int ret = 0;
int count = 0;
u8 reg_data = 0;
u8 msg_id = 0;
pr_info("%s enter", __func__);
mutex_lock(&pdic_data->_mutex);
/* if there is no attach, skip tx msg */
if (pdic_data->detach_valid)
goto done;
/* using msg id counter at S2MU004 */
s2mu004_usbpd_read_reg(pdic_data->i2c, S2MU004_REG_ID_MONITOR, &reg_data);
msg_id = reg_data & S2MU004_REG_ID_MONITOR_MSG_ID_MASK;
header->msg_id = msg_id;
ret = s2mu004_write_msg_header(i2c, header->byte);
if (ret < 0)
goto done;
count = header->num_data_objs;
if (count > 0) {
ret = s2mu004_write_msg_obj(i2c, count, obj);
if (ret < 0)
goto done;
}
s2mu004_send_msg(i2c);
pdic_data->status_reg = 0;
data->wait_for_msg_arrived = 0;
done:
mutex_unlock(&pdic_data->_mutex);
return ret;
}
static int s2mu004_rx_msg(void *_data,
msg_header_type *header, data_obj_type *obj)
{
struct usbpd_data *data = (struct usbpd_data *) _data;
struct s2mu004_usbpd_data *pdic_data = data->phy_driver_data;
int i;
int count = 0;
if (!s2mu004_receive_message(pdic_data)) {
header->word = pdic_data->header.word;
count = pdic_data->header.num_data_objs;
if (count > 0) {
for (i = 0; i < count; i++)
obj[i].object = pdic_data->obj[i].object;
}
pdic_data->header.word = 0; /* To clear for duplicated call */
return 0;
} else {
return -EINVAL;
}
}
static int s2mu004_set_otg_control(void *_data, int val)
{
struct usbpd_data *data = (struct usbpd_data *) _data;
struct s2mu004_usbpd_data *pdic_data = data->phy_driver_data;
if (val)
vbus_turn_on_ctrl(pdic_data, VBUS_ON);
else
vbus_turn_on_ctrl(pdic_data, VBUS_OFF);
return 0;
}
static int s2mu004_set_cc_control(void *_data, int val)
{
struct usbpd_data *data = (struct usbpd_data *) _data;
struct s2mu004_usbpd_data *pdic_data = data->phy_driver_data;
return s2mu004_usbpd_set_cc_control(pdic_data, val);
}
#if defined(CONFIG_TYPEC)
static void s2mu004_set_pwr_opmode(void *_data, int mode)
{
struct usbpd_data *data = (struct usbpd_data *) _data;
struct s2mu004_usbpd_data *pdic_data = data->phy_driver_data;
typec_set_pwr_opmode(pdic_data->port, mode);
}
#endif
static int s2mu004_set_rp_control(void *_data, int val)
{
return 0;
}
static int s2mu004_cc_instead_of_vbus(void *_data, int enable)
{
return 0;
}
static int s2mu004_op_mode_clear(void *_data)
{
return 0;
}
static int s2mu004_vbus_on_check(void *_data)
{
struct usbpd_data *data = (struct usbpd_data *) _data;
union power_supply_propval value;
if (data->pd_nego == false) {
pr_info("%s, skip by first attach\n", __func__);
return true;
}
if (!data->psy_muic) {
data->psy_muic = get_power_supply_by_name("muic-manager");
if (!data->psy_muic) {
pr_info("%s, fail to get psy_muic\n", __func__);
return true;
}
}
data->psy_muic->desc->get_property(data->psy_muic, POWER_SUPPLY_PROP_VBUS, &value);
return value.intval;
}
#if defined(CONFIG_CHECK_CTYPE_SIDE) || defined(CONFIG_CCIC_SYSFS)
static int s2mu004_get_side_check(void *_data)
{
struct usbpd_data *data = (struct usbpd_data *) _data;
struct s2mu004_usbpd_data *pdic_data = data->phy_driver_data;
struct i2c_client *i2c = pdic_data->i2c;
u8 val, cc1_val, cc2_val;
s2mu004_usbpd_test_read(pdic_data);
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_MON1, &val);
cc1_val = val & S2MU004_REG_CTRL_MON_CC1_MASK;
cc2_val = (val & S2MU004_REG_CTRL_MON_CC2_MASK) >> S2MU004_REG_CTRL_MON_CC2_SHIFT;
if (cc1_val == USBPD_Rd)
return USBPD_UP_SIDE;
else if (cc2_val == USBPD_Rd)
return USBPD_DOWN_SIDE;
else
return USBPD_UNDEFFINED_SIDE;
}
#endif
static int s2mu004_set_vconn_source(void *_data, int val)
{
struct usbpd_data *data = (struct usbpd_data *) _data;
struct s2mu004_usbpd_data *pdic_data = data->phy_driver_data;
struct i2c_client *i2c = pdic_data->i2c;
u8 reg_data = 0, reg_val = 0, cc1_val = 0, cc2_val = 0;
if (!pdic_data->vconn_en) {
pr_err("%s, not support vconn source\n", __func__);
return -1;
}
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_MON1, &reg_val);
cc1_val = (reg_val & S2MU004_REG_CTRL_MON_CC1_MASK) >> S2MU004_REG_CTRL_MON_CC1_SHIFT;
cc2_val = (reg_val & S2MU004_REG_CTRL_MON_CC2_MASK) >> S2MU004_REG_CTRL_MON_CC2_SHIFT;
if (val == USBPD_VCONN_ON) {
if (cc1_val == USBPD_Rd) {
if (cc2_val == USBPD_Ra) {
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_CTRL_RpRd, &reg_data);
reg_data &= ~S2MU004_REG_PLUG_CTRL_RpRd_VCONN_MASK;
reg_data |= (S2MU004_REG_PLUG_CTRL_RpRd_CC2_VCONN |
S2MU004_REG_PLUG_CTRL_VCONN_MANUAL_EN);
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_RpRd, reg_data);
}
}
if (cc2_val == USBPD_Rd) {
if (cc1_val == USBPD_Ra) {
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_CTRL_RpRd, &reg_data);
reg_data &= ~S2MU004_REG_PLUG_CTRL_RpRd_VCONN_MASK;
reg_data |= (S2MU004_REG_PLUG_CTRL_RpRd_CC1_VCONN |
S2MU004_REG_PLUG_CTRL_VCONN_MANUAL_EN);
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_RpRd, reg_data);
}
}
} else if (val == USBPD_VCONN_OFF) {
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_CTRL_RpRd, &reg_data);
reg_data &= ~S2MU004_REG_PLUG_CTRL_RpRd_VCONN_MASK;
reg_data |= S2MU004_REG_PLUG_CTRL_VCONN_MANUAL_EN;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_RpRd, reg_data);
} else
return(-1);
pdic_data->vconn_source = val;
return 0;
}
static void s2mu004_usbpd_set_vconn_manual(struct s2mu004_usbpd_data *pdic_data, bool enable)
{
u8 reg_data = 0;
s2mu004_usbpd_read_reg(pdic_data->i2c, S2MU004_REG_PLUG_CTRL_RpRd, &reg_data);
reg_data &= ~S2MU004_REG_PLUG_CTRL_RpRd_VCONN_MASK;
if (enable)
reg_data |= S2MU004_REG_PLUG_CTRL_VCONN_MANUAL_EN;
s2mu004_usbpd_write_reg(pdic_data->i2c, S2MU004_REG_PLUG_CTRL_RpRd, reg_data);
}
static int s2mu004_get_vconn_source(void *_data, int *val)
{
struct usbpd_data *data = (struct usbpd_data *) _data;
struct s2mu004_usbpd_data *pdic_data = data->phy_driver_data;
/* TODO
set s2mu004 pdic register control */
if (pdic_data->vconn_source != *val) {
dev_info(pdic_data->dev, "%s, vconn_source(%d) != gpio val(%d)\n",
__func__, pdic_data->vconn_source, *val);
pdic_data->vconn_source = *val;
}
return 0;
}
/* val : sink(0) or source(1) */
static int s2mu004_set_power_role(void *_data, int val)
{
struct usbpd_data *data = (struct usbpd_data *) _data;
struct s2mu004_usbpd_data *pdic_data = data->phy_driver_data;
pr_info("%s, power_role(%d)\n", __func__, val);
if (val == USBPD_SINK) {
pdic_data->is_pr_swap = true;
data->is_prswap = true;
s2mu004_assert_rd(data);
s2mu004_snk(pdic_data->i2c);
} else if (val == USBPD_SOURCE) {
pdic_data->is_pr_swap = true;
data->is_prswap = true;
s2mu004_assert_rp(data);
s2mu004_src(pdic_data->i2c);
} else if (val == USBPD_DRP) {
pdic_data->is_pr_swap = false;
data->is_prswap = false;
s2mu004_assert_drp(data);
return 0;
} else
return(-1);
pdic_data->power_role = val;
pr_info("%s, power_role(%d) DONE\n", __func__, val);
return 0;
}
static int s2mu004_get_power_role(void *_data, int *val)
{
struct usbpd_data *data = (struct usbpd_data *) _data;
struct s2mu004_usbpd_data *pdic_data = data->phy_driver_data;
*val = pdic_data->power_role;
return 0;
}
static int s2mu004_set_data_role(void *_data, int val)
{
struct usbpd_data *data = (struct usbpd_data *) _data;
struct s2mu004_usbpd_data *pdic_data = data->phy_driver_data;
struct i2c_client *i2c = pdic_data->i2c;
u8 val_port, data_role;
/* DATA_ROLE (0x18[2])
* 0 : UFP
* 1 : DFP
*/
if (val == USBPD_UFP) {
data_role = S2MU004_REG_MSG_DATA_ROLE_UFP;
s2mu004_ufp(i2c);
} else {/* (val == USBPD_DFP) */
data_role = S2MU004_REG_MSG_DATA_ROLE_DFP;
s2mu004_dfp(i2c);
}
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_CTRL_MSG, &val_port);
val_port = (val_port & ~S2MU004_REG_MSG_DATA_ROLE_MASK) | data_role;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_MSG, val_port);
pdic_data->data_role = val;
#if defined(CONFIG_CCIC_NOTIFIER)
process_dr_swap(pdic_data);
#endif
return 0;
}
static int s2mu004_get_data_role(void *_data, int *val)
{
struct usbpd_data *data = (struct usbpd_data *) _data;
struct s2mu004_usbpd_data *pdic_data = data->phy_driver_data;
*val = pdic_data->data_role;
return 0;
}
static void s2mu004_get_vbus_short_check(void *_data, bool *val)
{
struct usbpd_data *data = (struct usbpd_data *) _data;
struct s2mu004_usbpd_data *pdic_data = data->phy_driver_data;
*val = pdic_data->vbus_short;
}
static void s2mu004_pd_vbus_short_check(void *_data)
{
return;
}
static int s2mu004_set_check_msg_pass(void *_data, int val)
{
struct usbpd_data *data = (struct usbpd_data *) _data;
struct s2mu004_usbpd_data *pdic_data = data->phy_driver_data;
dev_info(pdic_data->dev, "%s: check_msg_pass val(%d)\n", __func__, val);
pdic_data->check_msg_pass = val;
return 0;
}
#ifndef CONFIG_SEC_FACTORY
static void s2mu004_usbpd_set_threshold(struct s2mu004_usbpd_data *pdic_data,
CCIC_RP_RD_SEL port_sel, CCIC_THRESHOLD_SEL threshold_sel)
{
struct i2c_client *i2c = pdic_data->i2c;
if (threshold_sel > S2MU004_THRESHOLD_MAX) {
dev_err(pdic_data->dev, "%s : threshold overflow!!\n", __func__);
return;
} else {
if (port_sel == PLUG_CTRL_RD)
s2mu004_usbpd_write_reg(i2c,
S2MU004_REG_PLUG_CTRL_SET_RD, threshold_sel);
else if (port_sel == PLUG_CTRL_RP)
s2mu004_usbpd_write_reg(i2c,
S2MU004_REG_PLUG_CTRL_SET_RP, threshold_sel);
}
}
static void s2mu004_usbpd_set_rp_scr_sel(struct s2mu004_usbpd_data *pdic_data,
CCIC_RP_SCR_SEL scr_sel)
{
struct i2c_client *i2c = pdic_data->i2c;
u8 data = 0;
pr_info("%s: scr_sel : (%d)\n", __func__, scr_sel);
switch (scr_sel) {
case PLUG_CTRL_RP80:
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_CTRL_PORT, &data);
data &= ~S2MU004_REG_PLUG_CTRL_RP_SEL_MASK;
data |= S2MU004_REG_PLUG_CTRL_RP80;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_PORT, data);
s2mu004_usbpd_set_threshold(pdic_data, PLUG_CTRL_RD,
S2MU004_THRESHOLD_214MV);
s2mu004_usbpd_set_threshold(pdic_data, PLUG_CTRL_RP,
S2MU004_THRESHOLD_1628MV);
break;
case PLUG_CTRL_RP180:
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_CTRL_PORT, &data);
data &= ~S2MU004_REG_PLUG_CTRL_RP_SEL_MASK;
data |= S2MU004_REG_PLUG_CTRL_RP180;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_PORT, data);
s2mu004_usbpd_set_threshold(pdic_data, PLUG_CTRL_RD,
S2MU004_THRESHOLD_428MV);
s2mu004_usbpd_set_threshold(pdic_data, PLUG_CTRL_RP,
S2MU004_THRESHOLD_2057MV);
break;
default:
break;
}
return;
}
#endif
void s2mu004_usbpd_check_msg(void *_data, u64 *val)
{
struct usbpd_data *data = (struct usbpd_data *) _data;
int data_type = 0;
int msg_type = 0;
int vdm_type = 0;
int vdm_command = 0;
u64 shift = 0;
u64 one = 1;
dev_info(data->dev, "%s\n", __func__);
if (data->protocol_rx.msg_header.num_data_objs == 0)
data_type = USBPD_CTRL_MSG;
else if (data->protocol_rx.msg_header.extended == 0)
data_type = USBPD_DATA_MSG;
else if (data->protocol_rx.msg_header.extended == 1)
data_type = USBPD_EXTENDED_MSG;
msg_type = data->protocol_rx.msg_header.msg_type;
/* Data Message */
if (data_type == USBPD_DATA_MSG) {
switch (msg_type) {
case USBPD_Source_Capabilities:
*val |= one << MSG_SRC_CAP;
break;
case USBPD_Sink_Capabilities:
*val |= one << MSG_SNK_CAP;
break;
case USBPD_Vendor_Defined:
vdm_command = data->protocol_rx.data_obj[0].structured_vdm.command;
vdm_type = data->protocol_rx.data_obj[0].structured_vdm.vdm_type;
if (vdm_type == Unstructured_VDM) {
if (data->protocol_rx.data_obj[0].unstructured_vdm.vendor_id != SAMSUNG_VENDOR_ID) {
*val |= one << MSG_RESERVED;
break;
}
dev_info(data->dev, "%s : uvdm msg received!\n", __func__);
*val |= one << UVDM_MSG;
break;
}
switch (vdm_command) {
case DisplayPort_Status_Update:
*val |= one << VDM_DP_STATUS_UPDATE;
break;
case DisplayPort_Configure:
*val |= one << VDM_DP_CONFIGURE;
break;
case Attention:
*val |= one << VDM_ATTENTION;
break;
case Exit_Mode:
*val |= one << VDM_EXIT_MODE;
break;
case Enter_Mode:
*val |= one << VDM_ENTER_MODE;
break;
case Discover_Modes:
*val |= one << VDM_DISCOVER_MODE;
break;
case Discover_SVIDs:
*val |= one << VDM_DISCOVER_SVID;
break;
case Discover_Identity:
*val |= one << VDM_DISCOVER_IDENTITY;
break;
default:
break;
}
break;
case 0: /* Reserved */
case 8 ... 0xe:
shift = MSG_RESERVED;
*val |= one << shift;
break;
}
}
dev_info(data->dev, "%s: msg status(%llu)\n", __func__, *val);
}
#ifdef CONFIG_CCIC_VDM
int s2mu004_usbpd_check_vdm_msg(void *_data, u64 *val)
{
struct usbpd_data *data = (struct usbpd_data *) _data;
int vdm_command = 0, vdm_type = 0;
dev_info(data->dev, "%s ++\n", __func__);
if (data->protocol_rx.msg_header.num_data_objs == 0) {
dev_info(data->dev, "%s data_obj null\n", __func__);
return 0;
}
if (data->protocol_rx.msg_header.msg_type != USBPD_Vendor_Defined) {
dev_info(data->dev, "%s msg type is wrong\n", __func__);
return 0;
}
vdm_command = data->protocol_rx.data_obj[0].structured_vdm.command;
vdm_type = data->protocol_rx.data_obj[0].structured_vdm.vdm_type;
if (vdm_type == Unstructured_VDM) {
dev_info(data->dev, "%s : uvdm msg received!\n", __func__);
*val |= 1 << UVDM_MSG;
return 0;
}
#if 0
switch (vdm_command) {
case DisplayPort_Status_Update:
*val |= VDM_DP_STATUS_UPDATE;
break;
case DisplayPort_Configure:
*val |= VDM_DP_CONFIGURE;
break;
default:
return 0;
}
#endif
dev_info(data->dev, "%s: check vdm mag val(%d)\n", __func__, vdm_command);
return 0;
}
#endif
static int s2mu004_usbpd_set_cc_control(struct s2mu004_usbpd_data *pdic_data, int val)
{
struct i2c_client *i2c = pdic_data->i2c;
u8 data = 0;
dev_info(pdic_data->dev, "%s, (%d)\n", __func__, val);
mutex_lock(&pdic_data->cc_mutex);
if (pdic_data->detach_valid)
goto out;
if (val) {
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_CTRL_CC12, &data);
data &= ~S2MU004_REG_PLUG_CTRL_CC_MANUAL_MASK;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_CC12, data);
} else {
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_CTRL_CC12, &data);
data &= ~S2MU004_REG_PLUG_CTRL_CC_MANUAL_MASK;
data |= S2MU004_REG_PLUG_CTRL_CC_MANUAL_EN;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_CC12, data);
}
out:
mutex_unlock(&pdic_data->cc_mutex);
return 0;
}
static void s2mu004_dfp(struct i2c_client *i2c)
{
u8 data;
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_CTRL_MSG, &data);
data |= S2MU004_REG_MSG_DATA_ROLE_MASK;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_MSG, data);
}
static void s2mu004_ufp(struct i2c_client *i2c)
{
u8 data;
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_CTRL_MSG, &data);
data &= ~S2MU004_REG_MSG_DATA_ROLE_MASK;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_MSG, data);
}
static void s2mu004_src(struct i2c_client *i2c)
{
u8 data;
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_CTRL_MSG, &data);
data = (data & ~S2MU004_REG_MSG_POWER_ROLE_MASK) | S2MU004_REG_MSG_POWER_ROLE_SOURCE;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_MSG, data);
}
static void s2mu004_snk(struct i2c_client *i2c)
{
u8 data;
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_CTRL_MSG, &data);
data = (data & ~S2MU004_REG_MSG_POWER_ROLE_MASK) | S2MU004_REG_MSG_POWER_ROLE_SINK;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_MSG, data);
}
#if defined(CONFIG_CCIC_NOTIFIER)
void s2mu004_control_option_command (struct s2mu004_usbpd_data *pdic_data, int cmd) {
struct usbpd_data *_data = dev_get_drvdata(pdic_data->dev);
int pd_cmd = cmd & 0x0f;
/* 0x1 : Vconn control option command ON
* 0x2 : Vconn control option command OFF
* 0x3 : Water Detect option command ON
* 0x4 : Water Detect option command OFF
*/
switch (pd_cmd) {
case 1:
s2mu004_set_vconn_source(_data, USBPD_VCONN_ON);
break;
case 2:
s2mu004_set_vconn_source(_data, USBPD_VCONN_OFF);
break;
case 3:
case 4:
pr_err("%s : not implement water control\n", __func__);
break;
default:
break;
}
}
#endif
static void s2mu004_notify_pdic_rid(struct s2mu004_usbpd_data *pdic_data, int rid)
{
#if defined(CONFIG_CCIC_NOTIFIER)
pdic_data->is_factory_mode = false;
if (rid == RID_523K)
pdic_data->is_factory_mode = true;
/* rid */
ccic_event_work(pdic_data,
CCIC_NOTIFY_DEV_MUIC, CCIC_NOTIFY_ID_RID, rid/*rid*/, 0);
if (rid == REG_RID_523K || rid == REG_RID_619K || rid == REG_RID_OPEN) {
ccic_event_work(pdic_data,
CCIC_NOTIFY_DEV_USB, CCIC_NOTIFY_ID_USB, 0/*attach*/, USB_STATUS_NOTIFY_DETACH);
pdic_data->is_host = HOST_OFF;
pdic_data->is_client = CLIENT_OFF;
} else if (rid == REG_RID_301K) {
ccic_event_work(pdic_data,
CCIC_NOTIFY_DEV_USB, CCIC_NOTIFY_ID_USB,
1/*attach*/, USB_STATUS_NOTIFY_ATTACH_UFP/*drp*/);
pdic_data->is_host = HOST_OFF;
pdic_data->is_client = CLIENT_ON;
}
#else
muic_attached_dev_t new_dev;
pdic_data->is_factory_mode = false;
switch (rid) {
case REG_RID_255K:
new_dev = ATTACHED_DEV_JIG_USB_OFF_MUIC;
break;
case REG_RID_301K:
new_dev = ATTACHED_DEV_JIG_USB_ON_MUIC;
break;
case REG_RID_523K:
new_dev = ATTACHED_DEV_JIG_UART_OFF_MUIC;
pdic_data->is_factory_mode = true;
break;
case REG_RID_619K:
new_dev = ATTACHED_DEV_JIG_UART_ON_MUIC;
break;
default:
new_dev = ATTACHED_DEV_NONE_MUIC;
return;
}
s2mu004_pdic_notifier_attach_attached_jig_dev(new_dev);
#endif
dev_info(pdic_data->dev, "%s : attached rid state(%d)", __func__, rid);
}
static void s2mu004_usbpd_check_rid(struct s2mu004_usbpd_data *pdic_data)
{
struct i2c_client *i2c = pdic_data->i2c;
u8 rid;
int prev_rid = pdic_data->rid;
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_ADC_STATUS, &rid);
rid = (rid & S2MU004_PDIC_RID_MASK) >> S2MU004_PDIC_RID_SHIFT;
dev_info(pdic_data->dev, "%s : attached rid state(%d)", __func__, rid);
if (rid) {
if (pdic_data->rid != rid) {
pdic_data->rid = rid;
if (prev_rid >= REG_RID_OPEN && rid >= REG_RID_OPEN)
dev_err(pdic_data->dev,
"%s : rid is not changed, skip notify(%d)", __func__, rid);
else
s2mu004_notify_pdic_rid(pdic_data, rid);
}
if (rid >= REG_RID_MAX) {
dev_err(pdic_data->dev, "%s : overflow rid value", __func__);
return;
}
}
}
#if (defined(CONFIG_DUAL_ROLE_USB_INTF) || defined(CONFIG_TYPEC))
static int s2mu004_set_attach(struct s2mu004_usbpd_data *pdic_data, u8 mode)
{
u8 data;
int ret = 0;
struct i2c_client *i2c = pdic_data->i2c;
struct device *dev = &i2c->dev;
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_CTRL_PORT, &data);
data &= ~(S2MU004_REG_PLUG_CTRL_MODE_MASK | S2MU004_REG_PLUG_CTRL_RP_SEL_MASK);
data |= mode | S2MU004_REG_PLUG_CTRL_RP180;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_PORT, data);
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PD_CTRL, &data);
data &= ~S2MU004_REG_LPM_EN;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PD_CTRL, data);
dev_info(dev, "%s s2mu004 force to attach\n", __func__);
return ret;
}
static int s2mu004_set_detach(struct s2mu004_usbpd_data *pdic_data, u8 mode)
{
u8 data;
int ret = 0;
struct i2c_client *i2c = pdic_data->i2c;
struct device *dev = &i2c->dev;
if (mode == TYPE_C_ATTACH_DFP) {
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_CTRL_RpRd, &data);
data |= S2MU004_REG_PLUG_CTRL_RpRd_Rp_Source_Mode;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_RpRd, data);
} else if (mode == TYPE_C_ATTACH_UFP) {
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_CTRL_RpRd, &data);
data |= S2MU004_REG_PLUG_CTRL_RpRd_Rd_Sink_Mode;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_RpRd, data);
}
msleep(50);
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_RpRd, S2MU004_RESET_REG_00);
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_CTRL_PORT, &data);
data &= ~(S2MU004_REG_PLUG_CTRL_MODE_MASK | S2MU004_REG_PLUG_CTRL_RP_SEL_MASK);
data |= S2MU004_REG_PLUG_CTRL_DFP | S2MU004_REG_PLUG_CTRL_RP0;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_PORT, data);
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PD_CTRL, &data);
data |= S2MU004_REG_LPM_EN;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PD_CTRL, data);
dev_info(dev, "%s s2mu004 force to detach\n", __func__);
return ret;
}
#endif
int s2mu004_set_normal_mode(struct s2mu004_usbpd_data *pdic_data)
{
u8 data;
u8 data_lpm;
int ret = 0;
struct i2c_client *i2c = pdic_data->i2c;
struct device *dev = &i2c->dev;
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_CTRL_PORT, &data);
data &= ~(S2MU004_REG_PLUG_CTRL_MODE_MASK | S2MU004_REG_PLUG_CTRL_RP_SEL_MASK);
data |= S2MU004_REG_PLUG_CTRL_DRP | S2MU004_REG_PLUG_CTRL_RP180;
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PD_CTRL, &data_lpm);
data_lpm &= ~S2MU004_REG_LPM_EN;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_PORT, data);
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PD_CTRL, data_lpm);
pdic_data->lpm_mode = false;
s2mu004_set_irq_enable(pdic_data, 0, 0,
S2MU004_REG_INT_STATUS2_MSG_SRC_CAP, ENABLED_INT_3,
ENABLED_INT_4, 0);
dev_info(dev, "%s s2mu004 exit lpm mode\n", __func__);
return ret;
}
int s2mu004_get_plug_monitor(struct s2mu004_usbpd_data *pdic_data, u8 *data)
{
u8 reg_val;
int ret = 0;
struct i2c_client *i2c = pdic_data->i2c;
if (&data[0] == NULL || &data[1] == NULL) {
pr_err("%s NULL point data\n", __func__);
return -1;
}
ret = s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_MON1, &reg_val);
if (ret < 0) {
pr_err("%s: S2MU004_REG_PLUG_MON1 Read err : %d\n", __func__, ret);
return ret;
}
data[0] = reg_val & S2MU004_REG_CTRL_MON_CC1_MASK;
data[1] = (reg_val & S2MU004_REG_CTRL_MON_CC2_MASK) >> S2MU004_REG_CTRL_MON_CC2_SHIFT;
pr_info("%s, water cc mon cc1 : 0x%X, cc2 : 0x%X\n", __func__, data[0], data[1]);
return ret;
}
int s2mu004_set_lpm_mode(struct s2mu004_usbpd_data *pdic_data)
{
u8 data, data_lpm;
int ret = 0;
struct i2c_client *i2c = pdic_data->i2c;
struct device *dev = &i2c->dev;
pdic_data->lpm_mode = true;
pdic_data->vbus_short_check_cnt = 0;
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_CTRL_PORT, &data);
data &= ~(S2MU004_REG_PLUG_CTRL_MODE_MASK | S2MU004_REG_PLUG_CTRL_RP_SEL_MASK);
data |= S2MU004_REG_PLUG_CTRL_DFP | S2MU004_REG_PLUG_CTRL_RP0;
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PD_CTRL, &data_lpm);
data_lpm |= S2MU004_REG_LPM_EN;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_PORT, data);
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PD_CTRL, data_lpm);
s2mu004_set_irq_enable(pdic_data, 0, 0, 0, 0, 0, 0);
dev_info(dev, "%s s2mu004 enter lpm mode\n", __func__);
return ret;
}
void s2mu004_set_water_detect_pre_cond(struct s2mu004_usbpd_data *pdic_data)
{
int i;
u8 cc_val[2] = {0,};
s2mu004_set_normal_mode(pdic_data);
mdelay(10);
for (i = 0; i < 14; i++) {
if (s2mu004_get_plug_monitor(pdic_data, cc_val) < 0) {
pr_info("%s abnormal", __func__);
mdelay(10);
} else {
if (IS_CC_RP(cc_val[0], cc_val[1]))
break;
else {
pr_info("%s Not Rp yet. ", __func__);
mdelay(10);
}
}
}
}
void s2mu004_set_water_1st_detect(struct s2mu004_usbpd_data *pdic_data)
{
u8 data, data_lpm;
struct i2c_client *i2c = pdic_data->i2c;
struct device *dev = &i2c->dev;
pdic_data->lpm_mode = true;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_SET_RP, S2MU004_THRESHOLD_MAX);
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_CTRL_PORT, &data);
data &= ~(S2MU004_REG_PLUG_CTRL_MODE_MASK | S2MU004_REG_PLUG_CTRL_RP_SEL_MASK);
data |= S2MU004_REG_PLUG_CTRL_DFP | S2MU004_REG_PLUG_CTRL_RP80
| S2MU004_REG_PLUG_CTRL_DETECT_BAT_DISABLE_MASK
| S2MU004_REG_PLUG_CTRL_DETECT_OCP_DISABLE_MASK;
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PD_CTRL, &data_lpm);
data_lpm |= S2MU004_REG_LPM_EN;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_PORT, data);
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PD_CTRL, data_lpm);
s2mu004_set_irq_enable(pdic_data, 0, 0, 0, 0, 0, 0);
usleep_range(500, 900);
data &= ~(S2MU004_REG_PLUG_CTRL_MODE_MASK | S2MU004_REG_PLUG_CTRL_RP_SEL_MASK);
data |= S2MU004_REG_PLUG_CTRL_DFP | S2MU004_REG_PLUG_CTRL_RP0;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_PORT, data);
dev_info(dev, "%s s2mu004 enter water chk lpm mode\n", __func__);
}
static bool s2mu004_is_water_detected_2nd_seq(struct s2mu004_usbpd_data *pdic_data, u8 *cc_val)
{
struct i2c_client *i2c = pdic_data->i2c;
u8 cc_chk[2] = {0,};
if (cc_val[0] == USBPD_Rp)
cc_chk[0] = 1;
if (cc_val[1] == USBPD_Rp)
cc_chk[1] = 1;
#if defined(CONFIG_CCIC_EXTERNAL_CAPACITOR)
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_SET_RP,
S2MU004_THRESHOLD_1587MV);
#else
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_SET_RD,
S2MU004_THRESHOLD_214MV);
#endif
s2mu004_set_lpm_mode(pdic_data);
s2mu004_usbpd_update_bit(i2c, S2MU004_REG_PD_CTRL,
S2MU004_REG_LPM_EN, 0, 0);
#if defined(CONFIG_CCIC_EXTERNAL_CAPACITOR)
msleep(520);
#else
msleep(300);
#endif
if (s2mu004_get_plug_monitor(pdic_data, cc_val) < 0) {
pr_err("%s Failed to get the plug monitor.\n", __func__);
return false;
}
/* Rp is detected due to the water CAPACITOR. */
#if defined(CONFIG_CCIC_EXTERNAL_CAPACITOR)
if ((cc_chk[0] && cc_chk[1]) && ((cc_val[0] == USBPD_Rp) && (cc_val[1] == USBPD_Rp)))
#else
if (((cc_chk[0] && !cc_chk[1]) && (cc_val[0] == USBPD_Rd)) ||
((cc_chk[1] && !cc_chk[0]) && (cc_val[1] == USBPD_Rd)) ||
((cc_chk[0] && cc_chk[1]) && ((cc_val[0] == USBPD_Rd) && (cc_val[1] == USBPD_Rd))))
#endif
{
return true;
}
return false;
}
static void _s2mu004_pdic_enter_to_water(struct s2mu004_usbpd_data *pdic_data)
{
struct i2c_client *i2c = pdic_data->i2c;
#if defined(CONFIG_USB_HW_PARAM) && !defined(CONFIG_USB_TYPEC_MANAGER_NOTIFIER)
struct otg_notify *o_notify = get_otg_notify();
#endif
#if defined(CONFIG_CCIC_NOTIFIER)
ccic_event_work(pdic_data,
CCIC_NOTIFY_DEV_MUIC, CCIC_NOTIFY_ID_WATER, CCIC_NOTIFY_ATTACH, 0);
#else
s2mu004_pdic_notifier_attach_attached_jig_dev(ATTACHED_DEV_WATER_MUIC);
#endif
pdic_data->is_water_detect = true;
pdic_data->water_detect_cnt = 0;
s2mu004_set_lpm_mode(pdic_data);
s2mu004_usbpd_update_bit(i2c, S2MU004_REG_PD_CTRL,
S2MU004_REG_LPM_EN, 0, 0);
#if defined(CONFIG_USB_HW_PARAM) && !defined(CONFIG_USB_TYPEC_MANAGER_NOTIFIER)
if (o_notify)
inc_hw_param(o_notify, USB_CCIC_WATER_INT_COUNT);
#endif
}
static void s2mu004_pdic_water_detect_handler(struct work_struct *work)
{
struct s2mu004_usbpd_data *pdic_data =
container_of(work, struct s2mu004_usbpd_data, water_detect_handler.work);
struct i2c_client *i2c = pdic_data->i2c;
u8 cc_val[2] = {0,};
pr_info("%s enter", __func__);
mutex_lock(&pdic_data->_mutex);
/*
* Cancel the detect handler,
* in case the muic notifies cable attach or dry signal,
* or the water chk cnt is over,
* or ccic already detected the water.
*/
if (!pdic_data->is_muic_water_detect
|| pdic_data->water_detect_cnt > WATER_CHK_RETRY_CNT
|| pdic_data->is_water_detect) {
pr_info("%s: detect handler is canceled", __func__);
goto WATER_OUT;
}
s2mu004_set_water_detect_pre_cond(pdic_data);
s2mu004_set_water_1st_detect(pdic_data);
msleep(400);
if (s2mu004_get_plug_monitor(pdic_data, cc_val) < 0) {
pr_err("%s Failed to get the plug monitor.\n", __func__);
goto WATER_MODE_OUT;
}
if (IS_CC_WATER(cc_val[0], cc_val[1])) {
pr_info("%s, water is detected, cc1 : 0x%X, cc2 : 0x%X\n",
__func__, cc_val[0], cc_val[1]);
_s2mu004_pdic_enter_to_water(pdic_data);
goto WATER_MODE_OUT;
} else {
pr_info("%s, 1st chk is not water, cc1 : 0x%X, cc2 : 0x%X\n",
__func__, cc_val[0], cc_val[1]);
if (s2mu004_is_water_detected_2nd_seq(pdic_data, cc_val)) {
pr_info("%s, 2nd seq, water is detected, cc1 : 0x%X, cc2 : 0x%X\n",
__func__, cc_val[0], cc_val[1]);
_s2mu004_pdic_enter_to_water(pdic_data);
goto WATER_MODE_OUT;
}
pr_info("%s, 2nd chk : not water, cc1 : 0x%X, cc2 : 0x%X\n",
__func__, cc_val[0], cc_val[1]);
if (pdic_data->water_detect_cnt++ >= WATER_CHK_RETRY_CNT) {
pdic_data->is_water_detect = false;
pdic_data->water_detect_cnt = 0;
#if defined(CONFIG_CCIC_NOTIFIER)
ccic_event_work(pdic_data,
CCIC_NOTIFY_DEV_MUIC, CCIC_NOTIFY_ID_WATER, CCIC_NOTIFY_DETACH, 0);
#endif
} else {
/*
* Retry the cc check,
* in case of invalid status.
* (200ms interval + 175ms check duration) * 5 times
*/
cancel_delayed_work(&pdic_data->water_detect_handler);
schedule_delayed_work(&pdic_data->water_detect_handler,
msecs_to_jiffies(S2MU004_WATER_CHK_INTERVAL_TIME));
}
}
WATER_MODE_OUT:
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_SET_RD,
S2MU004_THRESHOLD_428MV);
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_SET_RP,
S2MU004_THRESHOLD_2057MV);
WATER_OUT:
mutex_unlock(&pdic_data->_mutex);
return;
}
static void s2mu004_pdic_water_dry_handler(struct work_struct *work)
{
struct s2mu004_usbpd_data *pdic_data =
container_of(work, struct s2mu004_usbpd_data, water_dry_handler.work);
struct i2c_client *i2c = pdic_data->i2c;
u8 cc_val[2] = {0,};
pr_info("%s enter", __func__);
mutex_lock(&pdic_data->_mutex);
s2mu004_set_water_detect_pre_cond(pdic_data);
s2mu004_set_water_1st_detect(pdic_data);
msleep(400);
if (s2mu004_get_plug_monitor(pdic_data, cc_val) < 0) {
pr_err("%s Failed to get the plug monitor.\n", __func__);
}
if (IS_CC_RP(cc_val[0], cc_val[1])) {
pr_info("%s, 1st, water DRY is detected, cc1 : 0x%X, cc2 : 0x%X\n",
__func__, cc_val[0], cc_val[1]);
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_SET_RD,
S2MU004_THRESHOLD_428MV);
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_SET_RP,
S2MU004_THRESHOLD_2057MV);
#if defined(CONFIG_CCIC_NOTIFIER)
ccic_event_work(pdic_data,
CCIC_NOTIFY_DEV_MUIC, CCIC_NOTIFY_ID_WATER, CCIC_NOTIFY_DETACH, 0);
#endif
} else {
pr_info("%s, It is not DRIED yet, cc1 : 0x%X, cc2 : 0x%X\n",
__func__, cc_val[0], cc_val[1]);
#if defined(CONFIG_CCIC_NOTIFIER)
ccic_event_work(pdic_data,
CCIC_NOTIFY_DEV_MUIC, CCIC_NOTIFY_ID_WATER, CCIC_NOTIFY_ATTACH, 0);
#endif
s2mu004_set_lpm_mode(pdic_data);
s2mu004_usbpd_update_bit(i2c, S2MU004_REG_PD_CTRL,
S2MU004_REG_LPM_EN, 0, 0);
}
mutex_unlock(&pdic_data->_mutex);
return;
}
static void s2mu004_usbpd_otg_attach(struct s2mu004_usbpd_data *pdic_data)
{
#if defined(CONFIG_USB_HOST_NOTIFY)
struct otg_notify *o_notify = get_otg_notify();
#endif
struct i2c_client *i2c = pdic_data->i2c;
struct device *dev = &i2c->dev;
/* otg */
pdic_data->is_host = HOST_ON;
#if defined(CONFIG_DUAL_ROLE_USB_INTF)
pdic_data->power_role_dual = DUAL_ROLE_PROP_PR_SRC;
#elif defined(CONFIG_TYPEC)
pdic_data->typec_power_role = TYPEC_SOURCE;
typec_set_pwr_role(pdic_data->port, pdic_data->typec_power_role);
#endif
#if defined(CONFIG_USB_HOST_NOTIFY)
send_otg_notify(o_notify, NOTIFY_EVENT_POWER_SOURCE, 1);
#endif
/* USB */
ccic_event_work(pdic_data, CCIC_NOTIFY_DEV_USB, CCIC_NOTIFY_ID_USB,
1/*attach*/, USB_STATUS_NOTIFY_ATTACH_DFP/*drp*/);
/* add to turn on external 5V */
#if defined(CONFIG_USB_HOST_NOTIFY)
if (!is_blocked(o_notify, NOTIFY_BLOCK_TYPE_HOST))
vbus_turn_on_ctrl(pdic_data, VBUS_ON);
#endif
usbpd_manager_acc_handler_cancel(dev);
}
#if defined(CONFIG_MUIC_NOTIFIER)
static int type3_handle_notification(struct notifier_block *nb,
unsigned long action, void *data)
{
#if defined(CONFIG_CCIC_NOTIFIER)
CC_NOTI_ATTACH_TYPEDEF *p_noti = (CC_NOTI_ATTACH_TYPEDEF *)data;
muic_attached_dev_t attached_dev = p_noti->cable_type;
#else
muic_attached_dev_t attached_dev = *(muic_attached_dev_t *)data;
#endif
struct s2mu004_usbpd_data *pdic_data =
container_of(nb, struct s2mu004_usbpd_data,
type3_nb);
struct i2c_client *i2c = pdic_data->i2c;
u8 reg_data = 0;
#if (defined(CONFIG_USB_HW_PARAM) && !defined(CONFIG_USB_TYPEC_MANAGER_NOTIFIER)) || \
(!defined(CONFIG_SEC_FACTORY) && defined(CONFIG_USB_HOST_NOTIFY))
struct otg_notify *o_notify = get_otg_notify();
#endif
mutex_lock(&pdic_data->lpm_mutex);
pr_info("%s action:%d, attached_dev:%d, lpm:%d, pdic_data->is_otg_vboost:%d, pdic_data->is_otg_reboost:%d\n",
__func__, (int)action, (int)attached_dev, pdic_data->lpm_mode,
(int)pdic_data->is_otg_vboost, (int)pdic_data->is_otg_reboost);
if ((action == MUIC_PDIC_NOTIFY_CMD_ATTACH) &&
(attached_dev == ATTACHED_DEV_TYPE3_MUIC)) {
pdic_data->is_muic_water_detect = false;
pdic_data->water_detect_cnt = 0;
if (pdic_data->lpm_mode) {
pr_info("%s try to exit lpm mode-->\n", __func__);
s2mu004_set_normal_mode(pdic_data);
pr_info("%s after exit lpm mode<--\n", __func__);
}
} else if ((action == MUIC_PDIC_NOTIFY_CMD_ATTACH) &&
attached_dev == ATTACHED_DEV_CHK_WATER_REQ) {
pr_info("%s, ATTACH : MUIC REQUESTED WATER CHECK\n", __func__);
s2mu004_usbpd_set_vconn_manual(pdic_data, true);
pdic_data->is_muic_water_detect = true;
pdic_data->water_detect_cnt = 0;
pdic_data->is_water_detect = false;
cancel_delayed_work(&pdic_data->water_detect_handler);
schedule_delayed_work(&pdic_data->water_detect_handler, msecs_to_jiffies(100));
} else if ((action == MUIC_PDIC_NOTIFY_CMD_ATTACH) &&
attached_dev == ATTACHED_DEV_CHK_WATER_DRY_REQ) {
pr_info("%s, ATTACH : MUIC REQUESTED WATER DRY CHECK\n", __func__);
cancel_delayed_work(&pdic_data->water_dry_handler);
schedule_delayed_work(&pdic_data->water_dry_handler, msecs_to_jiffies(100));
} else if ((action == MUIC_PDIC_NOTIFY_CMD_ATTACH) &&
attached_dev == ATTACHED_DEV_OTG_MUIC) {
s2mu004_usbpd_otg_attach(pdic_data);
} else if ((action == MUIC_PDIC_NOTIFY_CMD_DETACH) &&
attached_dev == ATTACHED_DEV_UNDEFINED_RANGE_MUIC) {
pr_info("%s, DETACH : ATTACHED_DEV_UNDEFINED_RANGE_MUIC(Water DRY)\n", __func__);
pdic_data->is_muic_water_detect = false;
pdic_data->water_detect_cnt = 0;
pdic_data->is_water_detect = false;
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PD_CTRL, &reg_data);
reg_data |= S2MU004_REG_LPM_EN;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PD_CTRL, reg_data);
#if defined(CONFIG_USB_HW_PARAM) && !defined(CONFIG_USB_TYPEC_MANAGER_NOTIFIER)
if (o_notify)
inc_hw_param(o_notify, USB_CCIC_DRY_INT_COUNT);
#endif
#if defined(CONFIG_CCIC_NOTIFIER)
ccic_event_work(pdic_data,
CCIC_NOTIFY_DEV_MUIC, CCIC_NOTIFY_ID_WATER, CCIC_NOTIFY_DETACH, 0);
#endif
} else if (action == MUIC_PDIC_NOTIFY_CMD_DETACH) {
if (!pdic_data->lpm_mode) {
pr_info("%s try to enter lpm mode-->\n", __func__);
s2mu004_set_lpm_mode(pdic_data);
pr_info("%s after enter lpm mode<--\n", __func__);
}
}
#if !defined(CONFIG_SEC_FACTORY) && defined(CONFIG_USB_HOST_NOTIFY) && \
(defined(CONFIG_DUAL_ROLE_USB_INTF) || defined(CONFIG_TYPEC))
else if ((action == MUIC_PDIC_NOTIFY_CMD_ATTACH)
&& (attached_dev == ATTACHED_DEV_CHECK_OCP)
&& pdic_data->is_otg_vboost
#if defined(CONFIG_DUAL_ROLE_USB_INTF)
&& pdic_data->data_role_dual == USB_STATUS_NOTIFY_ATTACH_DFP
#elif defined(CONFIG_TYPEC)
&& pdic_data->typec_data_role == TYPEC_HOST
#endif
) {
if (o_notify) {
if (is_blocked(o_notify, NOTIFY_BLOCK_TYPE_HOST)) {
pr_info("%s, upsm mode, skip OCP handling\n", __func__);
goto EOH;
}
}
if (pdic_data->is_otg_reboost) {
/* todo : over current event to platform */
pr_info("%s, CHECK_OCP, Can't afford it(OVERCURRENT)\n", __func__);
if (o_notify)
send_otg_notify(o_notify, NOTIFY_EVENT_OVERCURRENT, 0);
goto EOH;
}
ccic_event_work(pdic_data,
CCIC_NOTIFY_DEV_MUIC, CCIC_NOTIFY_ID_ATTACH, 1/*attach*/, 1/*rprd*/);
pr_info("%s, CHECK_OCP, start OCP W/A\n", __func__);
pdic_data->is_otg_reboost = true;
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_CTRL_CC_HOLD, &reg_data);
reg_data |= S2MU004_REG_PLUG_CTRL_CC_HOLD_BIT;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_CC_HOLD, reg_data);
s2mu004_usbpd_set_rp_scr_sel(pdic_data, PLUG_CTRL_RP80);
vbus_turn_on_ctrl(pdic_data, VBUS_OFF);
vbus_turn_on_ctrl(pdic_data, VBUS_ON);
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_CTRL_CC_HOLD, &reg_data);
reg_data &= ~S2MU004_REG_PLUG_CTRL_CC_HOLD_BIT;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_CC_HOLD, reg_data);
}
EOH:
#endif
mutex_unlock(&pdic_data->lpm_mutex);
return 0;
}
#endif
#if (defined(CONFIG_DUAL_ROLE_USB_INTF) || defined(CONFIG_TYPEC))
static void s2mu004_usbpd_control_cc12_rd(struct s2mu004_usbpd_data *pdic_data,
bool enable)
{
struct i2c_client *i2c = pdic_data->i2c;
u8 data = 0;
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_CTRL_PORT, &data);
dev_info(pdic_data->dev, "%s, enable : %s current reg value(%x)\n", __func__,
enable ? "ON" : "OFF", data);
if (enable) {
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_CTRL_PORT, &data);
data &= ~S2MU004_REG_PLUG_CTRL_MODE_MASK;
data |= S2MU004_REG_PLUG_CTRL_UFP;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_PORT, data);
} else {
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_CTRL_PORT, &data);
data &= ~S2MU004_REG_PLUG_CTRL_MODE_MASK;
data |= S2MU004_REG_PLUG_CTRL_DRP;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_PORT, data);
}
}
#endif
static void s2mu004_vbus_short_check(struct s2mu004_usbpd_data *pdic_data)
{
struct i2c_client *i2c = pdic_data->i2c;
struct device *dev = pdic_data->dev;
u8 val = 0, prev_val = 0;
u8 cc1_val = 0, cc2_val = 0;
u8 rp_currentlvl = 0;
int retry = 0;
#if defined(CONFIG_USB_HW_PARAM)
struct otg_notify *o_notify = get_otg_notify();
#endif
if (pdic_data->vbus_short_check)
return;
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_CTRL_CC12, &val);
val &= ~S2MU004_REG_PLUG_CTRL_FSM_MANUAL_INPUT_MASK;
val |= S2MU004_REG_PLUG_CTRL_FSM_ATTACHED_SNK;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_CC12, val);
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_CTRL_RpRd, &val);
val |= S2MU004_REG_PLUG_CTRL_FSM_MANUAL_EN;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_RpRd, val);
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_SET_RP, S2MU004_THRESHOLD_1328MV);
msleep(20);
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_MON1, &val);
cc1_val = val & S2MU004_REG_CTRL_MON_CC1_MASK;
cc2_val = (val & S2MU004_REG_CTRL_MON_CC2_MASK) >> S2MU004_REG_CTRL_MON_CC2_SHIFT;
dev_info(dev, "%s, 10k check : cc1_val(%x), cc2_val(%x)\n",
__func__, cc1_val, cc2_val);
if (cc1_val == USBPD_Ra && cc2_val == USBPD_Ra)
rp_currentlvl = RP_CURRENT_LEVEL3;
else {
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_SET_RP, S2MU004_THRESHOLD_685MV);
msleep(20);
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_MON1, &val);
cc1_val = val & S2MU004_REG_CTRL_MON_CC1_MASK;
cc2_val = (val & S2MU004_REG_CTRL_MON_CC2_MASK) >> S2MU004_REG_CTRL_MON_CC2_SHIFT;
dev_info(dev, "%s, 56k check : cc1_val(%x), cc2_val(%x)\n",
__func__, cc1_val, cc2_val);
if (cc1_val == USBPD_Rd || cc2_val == USBPD_Rd)
rp_currentlvl = RP_CURRENT_LEVEL_DEFAULT;
else
rp_currentlvl = RP_CURRENT_LEVEL2;
}
#ifdef CONFIG_BATTERY_SAMSUNG
#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER
pd_noti.sink_status.rp_currentlvl = rp_currentlvl;
pd_noti.event = PDIC_NOTIFY_EVENT_CCIC_ATTACH;
ccic_event_work(pdic_data, CCIC_NOTIFY_DEV_BATTERY, CCIC_NOTIFY_ID_POWER_STATUS, 0, 0);
#endif
#endif
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_SET_RP, S2MU004_THRESHOLD_2057MV);
msleep(20);
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_CTRL_PORT, &val);
prev_val = val;
val &= ~S2MU004_REG_PLUG_CTRL_RP_SEL_MASK;
val |= S2MU004_REG_PLUG_CTRL_RP0;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_PORT, val);
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_CTRL_CC12, &val);
val &= ~S2MU004_REG_PLUG_CTRL_FSM_MANUAL_INPUT_MASK;
val |= S2MU004_REG_PLUG_CTRL_FSM_ATTACHED_SRC;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_CC12, val);
for (retry = 0; retry < 3; retry++) {
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_MON1, &val);
cc1_val = val & S2MU004_REG_CTRL_MON_CC1_MASK;
cc2_val = (val & S2MU004_REG_CTRL_MON_CC2_MASK) >> S2MU004_REG_CTRL_MON_CC2_SHIFT;
dev_info(dev, "%s, vbus short check(%d) : cc1_val(%x), cc2_val(%x)\n",
__func__, retry, cc1_val, cc2_val);
if (cc1_val == USBPD_Ra || cc2_val == USBPD_Ra) {
pdic_data->vbus_short = false;
if (rp_currentlvl == RP_CURRENT_LEVEL_DEFAULT)
ccic_event_work(pdic_data, CCIC_NOTIFY_DEV_MUIC,
CCIC_NOTIFY_ID_TA, 1/*attach*/, 0/*rprd*/);
break;
} else if (retry == 2) {
pdic_data->vbus_short = true;
#if defined(CONFIG_USB_HW_PARAM)
if (o_notify)
inc_hw_param(o_notify, USB_CCIC_VBUS_CC_SHORT_COUNT);
#endif
}
udelay(5);
}
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_CTRL_CC12, &val);
val &= ~S2MU004_REG_PLUG_CTRL_FSM_MANUAL_INPUT_MASK;
val |= S2MU004_REG_PLUG_CTRL_FSM_ATTACHED_SNK;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_CC12, val);
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_PORT, prev_val);
msleep(50);
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_CTRL_RpRd, &val);
val &= ~S2MU004_REG_PLUG_CTRL_FSM_MANUAL_EN;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_RpRd, val);
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_CTRL_CC12, &val);
val &= ~S2MU004_REG_PLUG_CTRL_FSM_MANUAL_INPUT_MASK;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_CC12, val);
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_INT_STATUS4, &val);
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_MON2, &val);
if ((val & S2MU004_PR_MASK) == S2MU004_PDIC_SINK) {
dev_err(dev, "%s, cc short check success (%x)\n", __func__, val);
pdic_data->vbus_short_check = true;
} else {
pdic_data->status_reg |= PLUG_ATTACH;
pdic_data->status_reg |= PLUG_DETACH;
pdic_data->vbus_short_check = false;
}
}
#ifndef CONFIG_SEC_FACTORY
static void s2mu004_power_off_water_check(struct s2mu004_usbpd_data *pdic_data)
{
struct i2c_client *i2c = pdic_data->i2c;
struct device *dev = pdic_data->dev;
u8 val, prev_val, data_lpm = 0;
u8 cc1_val, cc2_val;
int retry = 0;
mutex_lock(&pdic_data->_mutex);
mutex_lock(&pdic_data->lpm_mutex);
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_CTRL_PORT, &val);
prev_val = val;
val &= ~(S2MU004_REG_PLUG_CTRL_MODE_MASK | S2MU004_REG_PLUG_CTRL_RP_SEL_MASK);
val |= S2MU004_REG_PLUG_CTRL_RP0 | S2MU004_REG_PLUG_CTRL_DRP;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_PORT, val);
if (pdic_data->lpm_mode) {
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PD_CTRL, &data_lpm);
data_lpm &= ~S2MU004_REG_LPM_EN;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PD_CTRL, data_lpm);
}
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_CTRL_CC12, &val);
val &= ~S2MU004_REG_PLUG_CTRL_FSM_MANUAL_INPUT_MASK;
val |= S2MU004_REG_PLUG_CTRL_FSM_ATTACHED_SNK;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_CC12, val);
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_CTRL_RpRd, &val);
val |= S2MU004_REG_PLUG_CTRL_FSM_MANUAL_EN;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_RpRd, val);
msleep(50);
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_CTRL_CC12, &val);
val &= ~S2MU004_REG_PLUG_CTRL_FSM_MANUAL_INPUT_MASK;
val |= S2MU004_REG_PLUG_CTRL_FSM_ATTACHED_SRC;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_CC12, val);
usleep_range(1000, 1100);
for (retry = 0; retry < 3; retry++) {
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_MON1, &val);
cc1_val = val & S2MU004_REG_CTRL_MON_CC1_MASK;
cc2_val = (val & S2MU004_REG_CTRL_MON_CC2_MASK) >> S2MU004_REG_CTRL_MON_CC2_SHIFT;
dev_info(dev, "%s, vbus short check(%d) : cc1_val(%x), cc2_val(%x)\n",
__func__, retry, cc1_val, cc2_val);
if (cc1_val == USBPD_Ra || cc2_val == USBPD_Ra)
break;
else if (retry == 2) {
#if !IS_ENABLED(CONFIG_NONE_WATERPROOF_MODEL)
pdic_data->lpcharge_water = true;
pdic_data->is_water_detect = true;
pdic_data->water_detect_cnt = 0;
s2mu004_set_irq_enable(pdic_data, 0, 0, 0, 0, 0, 0);
s2mu004_usbpd_notify_detach(pdic_data);
ccic_event_work(pdic_data,
CCIC_NOTIFY_DEV_MUIC, CCIC_NOTIFY_ID_WATER, CCIC_NOTIFY_ATTACH, 1);
ccic_event_work(pdic_data, CCIC_NOTIFY_DEV_BATTERY, CCIC_NOTIFY_ID_WATER, 1, 0);
#else
dev_info(dev, "%s, detected but it's skipped.\n", __func__);
#endif
}
udelay(5);
}
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_CTRL_CC12, &val);
val &= ~S2MU004_REG_PLUG_CTRL_FSM_MANUAL_INPUT_MASK;
val |= S2MU004_REG_PLUG_CTRL_FSM_ATTACHED_SNK;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_CC12, val);
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_PORT, prev_val);
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_CTRL_RpRd, &val);
val &= ~S2MU004_REG_PLUG_CTRL_FSM_MANUAL_EN;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_RpRd, val);
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_CTRL_CC12, &val);
val &= ~S2MU004_REG_PLUG_CTRL_FSM_MANUAL_INPUT_MASK;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_CC12, val);
if (pdic_data->lpm_mode) {
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PD_CTRL, &data_lpm);
data_lpm |= S2MU004_REG_LPM_EN;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PD_CTRL, data_lpm);
}
mutex_unlock(&pdic_data->lpm_mutex);
mutex_unlock(&pdic_data->_mutex);
}
#endif
static void s2mu004_usbpd_detach_init(struct s2mu004_usbpd_data *pdic_data)
{
struct device *dev = pdic_data->dev;
struct usbpd_data *pd_data = dev_get_drvdata(dev);
struct i2c_client *i2c = pdic_data->i2c;
int ret = 0;
dev_info(dev, "%s\n", __func__);
s2mu004_usbpd_set_cc_control(pdic_data, USBPD_CC_OFF);
usbpd_manager_plug_detach(dev, 0);
pdic_data->status_reg = 0;
usbpd_reinit(dev);
/* for ccic hw detect */
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_MSG_SEND_CON, S2MU004_RESET_REG_00);
pdic_data->rid = REG_RID_MAX;
pdic_data->detach_valid = true;
pdic_data->is_factory_mode = false;
pdic_data->is_pr_swap = false;
pdic_data->vbus_short_check = false;
pdic_data->vbus_short = false;
if (pdic_data->regulator_en)
ret = regulator_disable(pdic_data->regulator);
#ifdef CONFIG_BATTERY_SAMSUNG
#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER
pd_noti.sink_status.current_pdo_num = 0;
pd_noti.sink_status.selected_pdo_num = 0;
pd_noti.sink_status.rp_currentlvl = RP_CURRENT_LEVEL_NONE;
#endif
#endif
s2mu004_usbpd_reg_init(pdic_data);
s2mu004_set_vconn_source(pd_data, USBPD_VCONN_OFF);
}
static void s2mu004_usbpd_notify_detach(struct s2mu004_usbpd_data *pdic_data)
{
struct device *dev = pdic_data->dev;
#if defined(CONFIG_CCIC_NOTIFIER)
#if defined(CONFIG_USB_HOST_NOTIFY)
struct otg_notify *o_notify = get_otg_notify();
#endif
/* MUIC */
if (pdic_data->check_first_detach == false) {
ccic_event_work(pdic_data, CCIC_NOTIFY_DEV_MUIC, CCIC_NOTIFY_ID_ATTACH,
0/*attach*/, 0/*rprd*/);
}
ccic_event_work(pdic_data, CCIC_NOTIFY_DEV_MUIC, CCIC_NOTIFY_ID_RID,
REG_RID_OPEN/*rid*/, 0);
#if defined(CONFIG_DUAL_ROLE_USB_INTF)
if (pdic_data->is_host > HOST_OFF || pdic_data->is_client > CLIENT_OFF) {
if (pdic_data->is_host > HOST_OFF ||
pdic_data->power_role_dual == DUAL_ROLE_PROP_PR_SRC) {
vbus_turn_on_ctrl(pdic_data, VBUS_OFF);
muic_disable_otg_detect();
}
#elif defined(CONFIG_TYPEC)
if (pdic_data->is_host > HOST_OFF || pdic_data->is_client > CLIENT_OFF) {
if (pdic_data->is_host > HOST_OFF ||
pdic_data->typec_power_role == TYPEC_SOURCE) {
vbus_turn_on_ctrl(pdic_data, VBUS_OFF);
muic_disable_otg_detect();
}
#endif
usbpd_manager_acc_detach(dev);
#if defined(CONFIG_DUAL_ROLE_USB_INTF)
pr_info("%s, data_role (%d)\n", __func__, pdic_data->data_role_dual);
if (pdic_data->data_role_dual == USB_STATUS_NOTIFY_ATTACH_DFP &&
!pdic_data->try_state_change) {
s2mu004_usbpd_control_cc12_rd(pdic_data, true);
msleep(S2MU004_WAIT_RD_DETACH_DELAY_MS);
s2mu004_usbpd_control_cc12_rd(pdic_data, false);
}
#elif defined(CONFIG_TYPEC)
pr_info("%s, data_role (%d)\n", __func__, pdic_data->typec_data_role);
if (pdic_data->typec_data_role == TYPEC_HOST &&
!pdic_data->typec_try_state_change) {
s2mu004_usbpd_control_cc12_rd(pdic_data, true);
msleep(S2MU004_WAIT_RD_DETACH_DELAY_MS);
s2mu004_usbpd_control_cc12_rd(pdic_data, false);
}
#endif
/* usb or otg */
dev_info(dev, "%s %d: is_host = %d, is_client = %d\n", __func__,
__LINE__, pdic_data->is_host, pdic_data->is_client);
pdic_data->is_host = HOST_OFF;
pdic_data->is_client = CLIENT_OFF;
#if defined(CONFIG_DUAL_ROLE_USB_INTF)
pdic_data->power_role_dual = DUAL_ROLE_PROP_PR_NONE;
#elif defined(CONFIG_TYPEC)
pdic_data->typec_power_role = TYPEC_SINK;
typec_set_pwr_role(pdic_data->port, TYPEC_SINK);
pdic_data->typec_data_role = TYPEC_DEVICE;
typec_set_data_role(pdic_data->port, TYPEC_DEVICE);
#endif
#if defined(CONFIG_USB_HOST_NOTIFY)
send_otg_notify(o_notify, NOTIFY_EVENT_POWER_SOURCE, 0);
#endif
/* USB */
ccic_event_work(pdic_data, CCIC_NOTIFY_DEV_USB, CCIC_NOTIFY_ID_USB,
0/*attach*/, USB_STATUS_NOTIFY_DETACH/*drp*/);
#if defined(CONFIG_DUAL_ROLE_USB_INTF)
if (!pdic_data->try_state_change)
s2mu004_rprd_mode_change(pdic_data, TYPE_C_ATTACH_DRP);
}
#elif defined(CONFIG_TYPEC)
if (!pdic_data->typec_try_state_change)
s2mu004_rprd_mode_change(pdic_data, TYPE_C_ATTACH_DRP);
}
#endif
#else
usbpd_manager_plug_detach(dev, 1);
#endif
}
static void s2mu004_usbpd_check_host(struct s2mu004_usbpd_data *pdic_data,
CCIC_HOST_REASON host)
{
#if defined(CONFIG_USB_HOST_NOTIFY)
struct otg_notify *o_notify = get_otg_notify();
#endif
if (host == HOST_ON && pdic_data->is_host == HOST_ON) {
dev_info(pdic_data->dev, "%s %d: turn off host\n", __func__, __LINE__);
ccic_event_work(pdic_data, CCIC_NOTIFY_DEV_MUIC,
CCIC_NOTIFY_ID_ATTACH, 0/*attach*/, 1/*rprd*/);
#if defined(CONFIG_DUAL_ROLE_USB_INTF)
pdic_data->power_role_dual = DUAL_ROLE_PROP_PR_NONE;
#elif defined(CONFIG_TYPEC)
pdic_data->typec_power_role = TYPEC_SINK;
typec_set_pwr_role(pdic_data->port, pdic_data->typec_power_role);
#endif
#if defined(CONFIG_USB_HOST_NOTIFY)
send_otg_notify(o_notify, NOTIFY_EVENT_POWER_SOURCE, 0);
#endif
/* add to turn off external 5V */
vbus_turn_on_ctrl(pdic_data, VBUS_OFF);
muic_disable_otg_detect();
ccic_event_work(pdic_data, CCIC_NOTIFY_DEV_USB, CCIC_NOTIFY_ID_USB,
0/*attach*/, USB_STATUS_NOTIFY_DETACH/*drp*/);
pdic_data->is_host = HOST_OFF;
msleep(300);
} else if (host == HOST_OFF && pdic_data->is_host == HOST_OFF) {
/* muic */
ccic_event_work(pdic_data, CCIC_NOTIFY_DEV_MUIC,
CCIC_NOTIFY_ID_OTG, 1/*attach*/, 0/*rprd*/);
}
}
static void s2mu004_usbpd_check_client(struct s2mu004_usbpd_data *pdic_data,
CCIC_DEVICE_REASON client)
{
if (client == CLIENT_ON && pdic_data->is_client == CLIENT_ON) {
dev_info(pdic_data->dev, "%s %d: turn off client\n", __func__, __LINE__);
ccic_event_work(pdic_data, CCIC_NOTIFY_DEV_MUIC,
CCIC_NOTIFY_ID_ATTACH, 0/*attach*/, 0/*rprd*/);
#if defined(CONFIG_DUAL_ROLE_USB_INTF)
pdic_data->power_role_dual = DUAL_ROLE_PROP_PR_NONE;
#elif defined(CONFIG_TYPEC)
pdic_data->typec_power_role = TYPEC_SINK;
typec_set_pwr_role(pdic_data->port, pdic_data->typec_power_role);
#endif
ccic_event_work(pdic_data, CCIC_NOTIFY_DEV_USB, CCIC_NOTIFY_ID_USB,
0/*attach*/, USB_STATUS_NOTIFY_DETACH/*drp*/);
pdic_data->is_client = CLIENT_OFF;
}
}
static int s2mu004_check_port_detect(struct s2mu004_usbpd_data *pdic_data)
{
struct i2c_client *i2c = pdic_data->i2c;
struct device *dev = &i2c->dev;
struct usbpd_data *pd_data = dev_get_drvdata(dev);
u8 data, val;
u8 cc1_val = 0, cc2_val = 0;
int ret = 0;
ret = s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_MON1, &val);
if (ret < 0)
dev_err(dev, "%s, i2c read PLUG_MON1 error\n", __func__);
cc1_val = val & S2MU004_REG_CTRL_MON_CC1_MASK;
cc2_val = (val & S2MU004_REG_CTRL_MON_CC2_MASK) >> S2MU004_REG_CTRL_MON_CC2_SHIFT;
pdic_data->cc1_val = cc1_val;
pdic_data->cc2_val = cc2_val;
ret = s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_MON2, &data);
if (ret < 0)
dev_err(dev, "%s, i2c read PLUG_MON2 error\n", __func__);
if ((data & S2MU004_PR_MASK) == S2MU004_PDIC_SINK) {
dev_info(dev, "SINK\n");
pdic_data->power_role = PDIC_SINK;
pdic_data->data_role = USBPD_UFP;
pdic_data->detach_valid = false;
s2mu004_snk(i2c);
s2mu004_ufp(i2c);
usbpd_policy_reset(pd_data, PLUG_EVENT);
#if defined(CONFIG_CCIC_NOTIFIER)
dev_info(&i2c->dev, "%s %d: is_host = %d, is_client = %d\n", __func__,
__LINE__, pdic_data->is_host, pdic_data->is_client);
if (pdic_data->regulator_en) {
ret = regulator_enable(pdic_data->regulator);
if (ret)
dev_err(&i2c->dev, "Failed to enable vconn LDO: %d\n", ret);
}
s2mu004_usbpd_check_host(pdic_data, HOST_ON);
/* muic */
ccic_event_work(pdic_data,
CCIC_NOTIFY_DEV_MUIC, CCIC_NOTIFY_ID_ATTACH, 1/*attach*/, 0/*rprd*/);
if (!(pdic_data->rid == REG_RID_523K || pdic_data->rid == REG_RID_619K)) {
if (pdic_data->is_client == CLIENT_OFF && pdic_data->is_host == HOST_OFF) {
/* usb */
pdic_data->is_client = CLIENT_ON;
#if defined(CONFIG_DUAL_ROLE_USB_INTF)
pdic_data->power_role_dual = DUAL_ROLE_PROP_PR_SNK;
#elif defined(CONFIG_TYPEC)
pdic_data->typec_power_role = TYPEC_SINK;
typec_set_pwr_role(pdic_data->port, pdic_data->typec_power_role);
#endif
ccic_event_work(pdic_data, CCIC_NOTIFY_DEV_USB, CCIC_NOTIFY_ID_USB,
1/*attach*/, USB_STATUS_NOTIFY_ATTACH_UFP/*drp*/);
}
}
#endif
s2mu004_vbus_short_check(pdic_data);
} else if ((data & S2MU004_PR_MASK) == S2MU004_PDIC_SOURCE) {
dev_info(dev, "SOURCE\n");
pdic_data->power_role = PDIC_SOURCE;
pdic_data->data_role = USBPD_DFP;
pdic_data->detach_valid = false;
s2mu004_dfp(i2c);
s2mu004_src(i2c);
usbpd_policy_reset(pd_data, PLUG_EVENT);
#if defined(CONFIG_CCIC_NOTIFIER)
dev_info(&i2c->dev, "%s %d: is_host = %d, is_client = %d\n", __func__,
__LINE__, pdic_data->is_host, pdic_data->is_client);
s2mu004_usbpd_check_client(pdic_data, CLIENT_ON);
s2mu004_usbpd_check_host(pdic_data, HOST_OFF);
#else
usbpd_manager_plug_attach(dev, ATTACHED_DEV_TYPE3_ADAPTER_MUIC);
#endif
if (pdic_data->regulator_en) {
ret = regulator_enable(pdic_data->regulator);
if (ret)
dev_err(&i2c->dev, "Failed to enable vconn LDO: %d\n", ret);
}
s2mu004_set_vconn_source(pd_data, USBPD_VCONN_ON);
s2mu004_assert_rp(pd_data);
msleep(100); /* dont over 310~620ms(tTypeCSinkWaitCap) */
s2mu004_assert_drp(pd_data);
} else {
dev_err(dev, "%s, PLUG Error\n", __func__);
return -1;
}
s2mu004_set_irq_enable(pdic_data, ENABLED_INT_0, ENABLED_INT_1,
ENABLED_INT_2, ENABLED_INT_3, ENABLED_INT_4, ENABLED_INT_5);
return ret;
}
static int s2mu004_check_init_port(struct s2mu004_usbpd_data *pdic_data)
{
u8 data;
int ret = 0;
struct i2c_client *i2c = pdic_data->i2c;
struct device *dev = &i2c->dev;
ret = s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_MON2, &data);
if (ret < 0)
dev_err(dev, "%s, i2c read PLUG_MON2 error\n", __func__);
if ((data & S2MU004_PR_MASK) == S2MU004_PDIC_SOURCE)
return PDIC_SOURCE;
else if ((data & S2MU004_PR_MASK) == S2MU004_PDIC_SINK)
return PDIC_SINK;
return -1;
}
#if defined(CONFIG_SEC_FACTORY)
static int s2mu004_usbpd_check_619k(struct s2mu004_usbpd_data *pdic_data)
{
u8 rid = 0;
if (pdic_data->rid != REG_RID_619K)
return false;
msleep(250);
s2mu004_usbpd_read_reg(pdic_data->i2c, S2MU004_REG_ADC_STATUS, &rid);
rid = (rid & S2MU004_PDIC_RID_MASK) >> S2MU004_PDIC_RID_SHIFT;
dev_info(pdic_data->dev, "%s %d: Detached, check if still 619K? => 0x%X\n",
__func__, __LINE__, rid);
if (rid == REG_RID_619K)
return true;
return false;
}
#endif
#ifndef CONFIG_SEC_FACTORY
static void s2mu004_usbpd_check_reboost(struct s2mu004_usbpd_data *pdic_data)
{
if (!pdic_data->is_otg_reboost)
return;
dev_info(pdic_data->dev, "%s %d: Detached, go back to 180uA\n",
__func__, __LINE__);
s2mu004_usbpd_set_rp_scr_sel(pdic_data, PLUG_CTRL_RP180);
pdic_data->is_otg_reboost = false;
return;
}
#endif
static irqreturn_t s2mu004_irq_thread(int irq, void *data)
{
struct s2mu004_usbpd_data *pdic_data = data;
struct i2c_client *i2c = pdic_data->i2c;
struct device *dev = &i2c->dev;
struct usbpd_data *pd_data = dev_get_drvdata(dev);
int ret = 0;
unsigned attach_status = 0, rid_status = 0;
dev_info(dev, "%s\n", __func__);
mutex_lock(&pd_data->accept_mutex);
mutex_unlock(&pd_data->accept_mutex);
mutex_lock(&pdic_data->_mutex);
s2mu004_poll_status(pd_data);
#ifndef CONFIG_SEC_FACTORY
if (pdic_data->lpcharge_water)
goto out;
#endif
if (s2mu004_get_status(pd_data, MSG_HARDRESET)) {
s2mu004_usbpd_set_cc_control(pdic_data, USBPD_CC_OFF);
s2mu004_self_soft_reset(i2c);
pdic_data->status_reg = 0;
if (pdic_data->power_role == PDIC_SOURCE)
s2mu004_dfp(i2c);
else
s2mu004_ufp(i2c);
usbpd_rx_hard_reset(dev);
usbpd_kick_policy_work(dev);
goto out;
}
if (s2mu004_get_status(pd_data, MSG_SOFTRESET))
usbpd_rx_soft_reset(pd_data);
if (s2mu004_get_status(pd_data, PLUG_DETACH)) {
#if defined(CONFIG_SEC_FACTORY)
ret = s2mu004_usbpd_check_619k(pdic_data);
if (ret)
goto skip_detach;
#endif /* CONFIG_SEC_FACTORY */
#ifndef CONFIG_SEC_FACTORY
s2mu004_usbpd_check_reboost(pdic_data);
#endif
attach_status = s2mu004_get_status(pd_data, PLUG_ATTACH);
rid_status = s2mu004_get_status(pd_data, MSG_RID);
s2mu004_usbpd_detach_init(pdic_data);
if (attach_status) {
ret = s2mu004_check_port_detect(pdic_data);
if (ret >= 0) {
if (rid_status) {
s2mu004_usbpd_check_rid(pdic_data);
}
goto hard_reset;
}
}
s2mu004_usbpd_notify_detach(pdic_data);
mutex_lock(&pdic_data->lpm_mutex);
if (!pdic_data->lpm_mode)
s2mu004_set_irq_enable(data, 0, 0,
S2MU004_REG_INT_STATUS2_MSG_SRC_CAP, 0, ENABLED_INT_4, 0);
mutex_unlock(&pdic_data->lpm_mutex);
goto out;
}
#if defined(CONFIG_SEC_FACTORY)
skip_detach:
#endif /* CONFIG_SEC_FACTORY */
if (s2mu004_get_status(pd_data, PLUG_ATTACH) && !pdic_data->is_pr_swap) {
if (s2mu004_check_port_detect(data) < 0)
goto out;
}
if (s2mu004_get_status(pd_data, MSG_RID)) {
s2mu004_usbpd_check_rid(pdic_data);
}
hard_reset:
mutex_lock(&pdic_data->lpm_mutex);
if (!pdic_data->lpm_mode)
usbpd_kick_policy_work(dev);
mutex_unlock(&pdic_data->lpm_mutex);
out:
mutex_unlock(&pdic_data->_mutex);
return IRQ_HANDLED;
}
static int s2mu004_usbpd_reg_init(struct s2mu004_usbpd_data *_data)
{
struct i2c_client *i2c = _data->i2c;
u8 data = 0;
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_CTRL, &data);
data |= S2MU004_REG_PLUG_CTRL_VDM_DISABLE;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL, data);
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PHY_CTRL_IFG, &data);
data |= S2MU004_PHY_IFG_35US << S2MU004_REG_IFG_SHIFT;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PHY_CTRL_IFG, data);
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PD_CTRL_2, &data);
data &= ~S2MU004_REG_CC_OCP_MASK;
data |= S2MU004_CC_OCP_575MV << S2MU004_REG_CC_OCP_SHIFT;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PD_CTRL_2, data);
/* set Rd threshold to 400mV */
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_SET_RD, S2MU004_THRESHOLD_428MV | 0x40);
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_SET_RP, S2MU004_THRESHOLD_2057MV);
if (_data->vconn_en) {
/* Off Manual Rd setup & On Manual Vconn setup */
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_PLUG_CTRL_RpRd, &data);
data &= ~(S2MU004_REG_PLUG_CTRL_RpRd_MANUAL_EN_MASK);
data |= S2MU004_REG_PLUG_CTRL_VCONN_MANUAL_EN;
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_RpRd, data);
}
s2mu004_usbpd_write_reg(i2c, S2MU004_REG_PLUG_CTRL_RpRd, S2MU004_RESET_REG_00);
s2mu004_usbpd_set_vconn_manual(_data, true);
return 0;
}
static irqreturn_t s2mu004_irq_isr(int irq, void *data)
{
return IRQ_WAKE_THREAD;
}
static int s2mu004_usbpd_irq_init(struct s2mu004_usbpd_data *_data)
{
struct i2c_client *i2c = _data->i2c;
struct device *dev = &i2c->dev;
int ret = 0;
if (!_data->irq_gpio) {
dev_err(dev, "%s No interrupt specified\n", __func__);
return -ENXIO;
}
/* s2mu004_usbpd_bulk_read(i2c, S2MU004_REG_INT_STATUS0,
S2MU004_MAX_NUM_INT_STATUS, intr);
pr_info("%s status[0x%x 0x%x 0x%x 0x%x 0x%x]\n",
__func__, intr[0], intr[1], intr[2], intr[3], intr[4]);
*/
i2c->irq = gpio_to_irq(_data->irq_gpio);
if (i2c->irq) {
ret = request_threaded_irq(i2c->irq, s2mu004_irq_isr,
s2mu004_irq_thread,
(IRQF_TRIGGER_LOW | IRQF_ONESHOT | IRQF_NO_SUSPEND),
"s2mu004-usbpd", _data);
if (ret < 0) {
dev_err(dev, "%s failed to request irq(%d)\n",
__func__, i2c->irq);
return ret;
}
ret = enable_irq_wake(i2c->irq);
if (ret < 0)
dev_err(dev, "%s failed to enable wakeup src\n",
__func__);
}
if (_data->lpm_mode)
s2mu004_set_irq_enable(_data, 0, 0, 0, 0, 0, 0);
else
s2mu004_set_irq_enable(_data, 0, 0, 0, 0, ENABLED_INT_4, 0);
return ret;
}
static void s2mu004_usbpd_init_configure(struct s2mu004_usbpd_data *_data)
{
struct i2c_client *i2c = _data->i2c;
struct device *dev = _data->dev;
u8 rid = 0;
int pdic_port = 0;
s2mu004_usbpd_read_reg(i2c, S2MU004_REG_ADC_STATUS, &rid);
rid = (rid & S2MU004_PDIC_RID_MASK) >> S2MU004_PDIC_RID_SHIFT;
_data->rid = rid;
_data->detach_valid = false;
_data->check_first_detach = true;
/* if there is rid, assume that booted by normal mode */
if (rid) {
_data->lpm_mode = false;
_data->is_factory_mode = false;
if (factory_mode) {
if (rid != REG_RID_523K) {
dev_err(dev, "%s : In factory mode, but RID is not 523K\n", __func__);
} else {
dev_err(dev, "%s : In factory mode, but RID is 523K OK\n", __func__);
_data->is_factory_mode = true;
}
}
_data->check_first_detach = false;
s2mu004_usbpd_set_cc_control(_data, USBPD_CC_ON);
} else {
dev_err(dev, "%s : Initial abnormal state to LPM Mode\n",
__func__);
pdic_port = s2mu004_check_init_port(_data);
s2mu004_set_lpm_mode(_data);
s2mu004_usbpd_set_cc_control(_data, USBPD_CC_OFF);
_data->lpm_mode = true;
msleep(150); /* for abnormal PD TA */
_data->is_factory_mode = false;
#if defined(CONFIG_CCIC_MODE_WITHOUT_MUIC)
s2mu004_set_normal_mode(_data);
#else
if (pdic_port == PDIC_SOURCE) {
s2mu004_set_normal_mode(_data);
_data->check_first_detach = false;
}
#endif
}
}
static void s2mu004_usbpd_pdic_data_init(struct s2mu004_usbpd_data *_data)
{
_data->check_msg_pass = false;
_data->vconn_source = USBPD_VCONN_OFF;
_data->rid = REG_RID_MAX;
_data->is_host = 0;
_data->is_client = 0;
#if defined(CONFIG_DUAL_ROLE_USB_INTF)
_data->data_role_dual = 0;
_data->power_role_dual = 0;
#elif defined(CONFIG_TYPEC)
_data->typec_power_role = TYPEC_SINK;
_data->typec_data_role = TYPEC_DEVICE;
#endif
_data->is_water_detect = false;
_data->is_muic_water_detect = false;
_data->detach_valid = true;
_data->is_otg_vboost = false;
_data->is_otg_reboost = false;
_data->is_pr_swap = false;
_data->vbus_short = false;
_data->vbus_short_check = false;
_data->vbus_short_check_cnt = 0;
#ifndef CONFIG_SEC_FACTORY
_data->lpcharge_water = false;
#endif
}
static int of_s2mu004_dt(struct device *dev,
struct s2mu004_usbpd_data *_data)
{
struct device_node *np_usbpd = dev->of_node;
int ret = 0;
if (np_usbpd == NULL) {
dev_err(dev, "%s np NULL\n", __func__);
return -EINVAL;
} else {
_data->irq_gpio = of_get_named_gpio(np_usbpd,
"usbpd,usbpd_int", 0);
if (_data->irq_gpio < 0) {
dev_err(dev, "error reading usbpd irq = %d\n",
_data->irq_gpio);
_data->irq_gpio = 0;
}
if (of_find_property(np_usbpd, "vconn-en", NULL))
_data->vconn_en = true;
else
_data->vconn_en = false;
if (of_find_property(np_usbpd, "regulator-en", NULL))
_data->regulator_en = true;
else
_data->regulator_en = false;
}
return ret;
}
static int s2mu004_usbpd_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct i2c_adapter *adapter = to_i2c_adapter(i2c->dev.parent);
struct s2mu004_usbpd_data *pdic_data;
struct device *dev = &i2c->dev;
int ret = 0;
dev_info(dev, "%s\n", __func__);
test_i2c = i2c;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
dev_err(dev, "%s: i2c functionality check error\n", __func__);
ret = -EIO;
goto err_return;
}
pdic_data = kzalloc(sizeof(struct s2mu004_usbpd_data), GFP_KERNEL);
if (!pdic_data) {
dev_err(dev, "%s: failed to allocate driver data\n", __func__);
ret = -ENOMEM;
goto err_return;
}
/* save platfom data for gpio control functions */
pdic_data->dev = &i2c->dev;
pdic_data->i2c = i2c;
i2c_set_clientdata(i2c, pdic_data);
ret = of_s2mu004_dt(&i2c->dev, pdic_data);
if (ret < 0)
dev_err(dev, "%s: not found dt!\n", __func__);
mutex_init(&pdic_data->_mutex);
mutex_init(&pdic_data->lpm_mutex);
mutex_init(&pdic_data->cc_mutex);
s2mu004_usbpd_init_configure(pdic_data);
s2mu004_usbpd_pdic_data_init(pdic_data);
if (pdic_data->regulator_en) {
pdic_data->regulator = devm_regulator_get(dev, "vconn");
if (IS_ERR(pdic_data->regulator)) {
dev_err(dev, "%s: not found regulator vconn\n", __func__);
pdic_data->regulator_en = false;
} else
ret = regulator_disable(pdic_data->regulator);
}
ret = usbpd_init(dev, pdic_data);
if (ret < 0) {
dev_err(dev, "failed on usbpd_init\n");
goto err_return;
}
usbpd_set_ops(dev, &s2mu004_ops);
s2mu004_usbpd_reg_init(pdic_data);
pdic_data->pdic_queue =
alloc_workqueue(dev_name(dev), WQ_MEM_RECLAIM, 1);
if (!pdic_data->pdic_queue) {
dev_err(dev,
"%s: Fail to Create Workqueue\n", __func__);
goto err_return;
}
#if defined(CONFIG_CCIC_NOTIFIER)
ccic_notifier_init();
/* Create a work queue for the ccic irq thread */
pdic_data->ccic_wq
= create_singlethread_workqueue("ccic_irq_event");
if (!pdic_data->ccic_wq) {
pr_err("%s failed to create work queue for ccic notifier\n",
__func__);
goto err_return;
}
if (pdic_data->rid == REG_RID_UNDF)
pdic_data->rid = REG_RID_MAX;
dev_set_drvdata(ccic_device, pdic_data);
#endif
#if defined(CONFIG_TYPEC)
ret = typec_init(pdic_data);
if (ret < 0) {
pr_err("failed to init typec\n");
goto err_return;
}
#endif
ret = s2mu004_usbpd_irq_init(pdic_data);
if (ret) {
dev_err(dev, "%s: failed to init irq(%d)\n", __func__, ret);
goto fail_init_irq;
}
INIT_DELAYED_WORK(&pdic_data->water_detect_handler, s2mu004_pdic_water_detect_handler);
INIT_DELAYED_WORK(&pdic_data->water_dry_handler, s2mu004_pdic_water_dry_handler);
if (pdic_data->detach_valid) {
mutex_lock(&pdic_data->_mutex);
s2mu004_check_port_detect(pdic_data);
s2mu004_usbpd_check_rid(pdic_data);
mutex_unlock(&pdic_data->_mutex);
}
#ifndef CONFIG_SEC_FACTORY
if (lpcharge)
s2mu004_power_off_water_check(pdic_data);
#endif
s2mu004_irq_thread(-1, pdic_data);
pdic_data->check_first_detach = false;
#if defined(CONFIG_MUIC_NOTIFIER)
muic_ccic_notifier_register(&pdic_data->type3_nb,
type3_handle_notification,
MUIC_NOTIFY_DEV_PDIC);
#endif
#if defined(CONFIG_DUAL_ROLE_USB_INTF)
ret = dual_role_init(pdic_data);
if (ret < 0) {
pr_err("unable to allocate dual role descriptor\n");
goto fail_init_irq;
}
#endif
dev_info(dev, "%s s2mu004 usbpd driver uploaded!\n", __func__);
return 0;
fail_init_irq:
if (i2c->irq)
free_irq(i2c->irq, pdic_data);
err_return:
return ret;
}
#if defined CONFIG_PM
static int s2mu004_usbpd_suspend(struct device *dev)
{
struct usbpd_data *_data = dev_get_drvdata(dev);
struct s2mu004_usbpd_data *pdic_data = _data->phy_driver_data;
if (device_may_wakeup(dev))
enable_irq_wake(pdic_data->i2c->irq);
#ifndef CONFIG_SEC_FACTORY
disable_irq(pdic_data->i2c->irq);
#endif
return 0;
}
static int s2mu004_usbpd_resume(struct device *dev)
{
struct usbpd_data *_data = dev_get_drvdata(dev);
struct s2mu004_usbpd_data *pdic_data = _data->phy_driver_data;
if (device_may_wakeup(dev))
disable_irq_wake(pdic_data->i2c->irq);
#ifndef CONFIG_SEC_FACTORY
enable_irq(pdic_data->i2c->irq);
#endif
return 0;
}
#else
#define s2mu004_muic_suspend NULL
#define s2mu004_muic_resume NULL
#endif
static int s2mu004_usbpd_remove(struct i2c_client *i2c)
{
struct s2mu004_usbpd_data *_data = i2c_get_clientdata(i2c);
if (_data) {
#if defined(CONFIG_DUAL_ROLE_USB_INTF)
devm_dual_role_instance_unregister(_data->dev,
_data->dual_role);
devm_kfree(_data->dev, _data->desc);
#elif defined(CONFIG_TYPEC)
typec_unregister_port(_data->port);
#endif
disable_irq_wake(_data->i2c->irq);
free_irq(_data->i2c->irq, _data);
mutex_destroy(&_data->_mutex);
i2c_set_clientdata(_data->i2c, NULL);
kfree(_data);
}
return 0;
}
static const struct i2c_device_id s2mu004_usbpd_i2c_id[] = {
{ USBPD_DEV_NAME, 1 },
{}
};
MODULE_DEVICE_TABLE(i2c, s2mu004_i2c_id);
static struct of_device_id sec_usbpd_i2c_dt_ids[] = {
{ .compatible = "sec-usbpd,i2c" },
{ }
};
static void s2mu004_usbpd_shutdown(struct i2c_client *i2c)
{
struct s2mu004_usbpd_data *_data = i2c_get_clientdata(i2c);
if (!_data->i2c)
return;
}
static usbpd_phy_ops_type s2mu004_ops = {
.tx_msg = s2mu004_tx_msg,
.rx_msg = s2mu004_rx_msg,
.hard_reset = s2mu004_hard_reset,
.soft_reset = s2mu004_soft_reset,
.set_power_role = s2mu004_set_power_role,
.get_power_role = s2mu004_get_power_role,
.set_data_role = s2mu004_set_data_role,
.get_data_role = s2mu004_get_data_role,
.set_vconn_source = s2mu004_set_vconn_source,
.get_vconn_source = s2mu004_get_vconn_source,
.set_check_msg_pass = s2mu004_set_check_msg_pass,
.get_status = s2mu004_get_status,
.poll_status = s2mu004_poll_status,
.driver_reset = s2mu004_driver_reset,
.set_otg_control = s2mu004_set_otg_control,
.get_vbus_short_check = s2mu004_get_vbus_short_check,
.pd_vbus_short_check = s2mu004_pd_vbus_short_check,
.set_cc_control = s2mu004_set_cc_control,
#if defined(CONFIG_CHECK_CTYPE_SIDE) || defined(CONFIG_CCIC_SYSFS)
.get_side_check = s2mu004_get_side_check,
#endif
.pr_swap = s2mu004_pr_swap,
.vbus_on_check = s2mu004_vbus_on_check,
.set_rp_control = s2mu004_set_rp_control,
.cc_instead_of_vbus = s2mu004_cc_instead_of_vbus,
.op_mode_clear = s2mu004_op_mode_clear,
#if defined(CONFIG_TYPEC)
.set_pwr_opmode = s2mu004_set_pwr_opmode,
#endif
};
#if defined CONFIG_PM
const struct dev_pm_ops s2mu004_usbpd_pm = {
.suspend = s2mu004_usbpd_suspend,
.resume = s2mu004_usbpd_resume,
};
#endif
static struct i2c_driver s2mu004_usbpd_driver = {
.driver = {
.name = USBPD_DEV_NAME,
.of_match_table = sec_usbpd_i2c_dt_ids,
#if defined CONFIG_PM
.pm = &s2mu004_usbpd_pm,
#endif /* CONFIG_PM */
},
.probe = s2mu004_usbpd_probe,
.remove = s2mu004_usbpd_remove,
.shutdown = s2mu004_usbpd_shutdown,
.id_table = s2mu004_usbpd_i2c_id,
};
static int __init s2mu004_usbpd_init(void)
{
return i2c_add_driver(&s2mu004_usbpd_driver);
}
late_initcall(s2mu004_usbpd_init);
static void __exit s2mu004_usbpd_exit(void)
{
i2c_del_driver(&s2mu004_usbpd_driver);
}
module_exit(s2mu004_usbpd_exit);
MODULE_DESCRIPTION("S2MU004 USB PD driver");
MODULE_LICENSE("GPL");