blob: 17c1b77a00cc0b81d6d5013c52aa9194275dc1b7 [file] [log] [blame]
/*
* driver/../s2mm005.c - S2MM005 USBPD device driver
*
* Copyright (C) 2015 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/ccic/s2mm005.h>
#include <linux/ccic/s2mm005_ext.h>
#include <linux/ccic/s2mm005_fw.h>
#include <linux/usb_notify.h>
#include <linux/ccic/ccic_sysfs.h>
#if defined(CONFIG_CCIC_NOTIFIER)
#include <linux/ccic/ccic_sysfs.h>
#include <linux/ccic/ccic_core.h>
#include <linux/ccic/ccic_notifier.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/regulator/consumer.h>
static enum ccic_sysfs_property s2mm005_sysfs_properties[] = {
CCIC_SYSFS_PROP_CHIP_NAME,
CCIC_SYSFS_PROP_CUR_VERSION,
CCIC_SYSFS_PROP_SRC_VERSION,
CCIC_SYSFS_PROP_LPM_MODE,
CCIC_SYSFS_PROP_STATE,
CCIC_SYSFS_PROP_RID,
CCIC_SYSFS_PROP_CTRL_OPTION,
CCIC_SYSFS_PROP_BOOTING_DRY,
CCIC_SYSFS_PROP_FW_UPDATE,
CCIC_SYSFS_PROP_FW_UPDATE_STATUS,
CCIC_SYSFS_PROP_FW_WATER,
CCIC_SYSFS_PROP_DEX_FAN_UVDM,
CCIC_SYSFS_PROP_CONTROL_GPIO,
CCIC_SYSFS_PROP_USBPD_IDS,
CCIC_SYSFS_PROP_USBPD_TYPE,
};
#endif /* CONFIG_CCIC_NOTIFIER */
extern struct pdic_notifier_struct pd_noti;
#if defined(CONFIG_BATTERY_SAMSUNG)
extern unsigned int lpcharge;
#endif
#if defined(CONFIG_DUAL_ROLE_USB_INTF)
static enum dual_role_property fusb_drp_properties[] = {
DUAL_ROLE_PROP_MODE,
DUAL_ROLE_PROP_PR,
DUAL_ROLE_PROP_DR,
};
#endif
////////////////////////////////////////////////////////////////////////////////
// function definition
////////////////////////////////////////////////////////////////////////////////
void s2mm005_int_clear(struct s2mm005_data *usbpd_data);
int s2mm005_read_byte(const struct i2c_client *i2c, u16 reg, u8 *val, u16 size);
int s2mm005_read_byte_flash(const struct i2c_client *i2c, u16 reg, u8 *val, u16 size);
int s2mm005_write_byte(const struct i2c_client *i2c, u16 reg, u8 *val, u16 size);
int s2mm005_read_byte_16(const struct i2c_client *i2c, u16 reg, u8 *val);
int s2mm005_write_byte_16(const struct i2c_client *i2c, u16 reg, u8 val);
void s2mm005_rprd_mode_change(struct s2mm005_data *usbpd_data, u8 mode);
void s2mm005_manual_JIGON(struct s2mm005_data *usbpd_data, int mode);
void s2mm005_manual_LPM(struct s2mm005_data *usbpd_data, int cmd);
void s2mm005_control_option_command(struct s2mm005_data *usbpd_data, int cmd);
int s2mm005_fw_ver_check(void *data);
////////////////////////////////////////////////////////////////////////////////
//status machine of s2mm005 ccic
////////////////////////////////////////////////////////////////////////////////
//enum ccic_status {
// state_cc_unknown = 0,
// state_cc_idle,
// state_cc_rid,
// state_cc_updatefw,
// state_cc_alternate,
// state_cc_end=0xff,
//};
////////////////////////////////////////////////////////////////////////////////
extern void (*fp_select_pdo)(int num);
int s2mm005_read_byte(const struct i2c_client *i2c, u16 reg, u8 *val, u16 size)
{
int ret, i2c_retry; u8 wbuf[2];
struct i2c_msg msg[2];
struct s2mm005_data *usbpd_data = i2c_get_clientdata(i2c);
#if defined(CONFIG_USB_HW_PARAM)
struct otg_notify *o_notify = get_otg_notify();
#endif
mutex_lock(&usbpd_data->i2c_mutex);
i2c_retry = 0;
msg[0].addr = i2c->addr;
msg[0].flags = i2c->flags;
msg[0].len = 2;
msg[0].buf = wbuf;
msg[1].addr = i2c->addr;
msg[1].flags = I2C_M_RD;
msg[1].len = size;
msg[1].buf = val;
wbuf[0] = (reg & 0xFF00) >> 8;
wbuf[1] = (reg & 0xFF);
do {
ret = i2c_transfer(i2c->adapter, msg, ARRAY_SIZE(msg));
} while (ret < 0 && i2c_retry++ < 5);
if (ret < 0) {
#if defined(CONFIG_USB_HW_PARAM)
if (o_notify)
inc_hw_param(o_notify, USB_CCIC_I2C_ERROR_COUNT);
#endif
dev_err(&i2c->dev, "i2c read16 fail reg:0x%x error %d\n", reg, ret);
}
mutex_unlock(&usbpd_data->i2c_mutex);
return ret;
}
int s2mm005_read_byte_flash(const struct i2c_client *i2c, u16 reg, u8 *val, u16 size)
{
int ret; u8 wbuf[2];
struct i2c_msg msg[2];
struct s2mm005_data *usbpd_data = i2c_get_clientdata(i2c);
#if defined(CONFIG_USB_HW_PARAM)
struct otg_notify *o_notify = get_otg_notify();
#endif
u8 W_DATA[1];
udelay(20);
W_DATA[0] = 0xAA;
s2mm005_write_byte(i2c, 0x10, &W_DATA[0], 1);
udelay(20);
mutex_lock(&usbpd_data->i2c_mutex);
msg[0].addr = i2c->addr;
msg[0].flags = i2c->flags;
msg[0].len = 2;
msg[0].buf = wbuf;
msg[1].addr = i2c->addr;
msg[1].flags = I2C_M_RD;
msg[1].len = size;
msg[1].buf = val;
wbuf[0] = (reg & 0xFF00) >> 8;
wbuf[1] = (reg & 0xFF);
ret = i2c_transfer(i2c->adapter, msg, ARRAY_SIZE(msg));
if (ret < 0) {
#if defined(CONFIG_USB_HW_PARAM)
if (o_notify)
inc_hw_param(o_notify, USB_CCIC_I2C_ERROR_COUNT);
#endif
dev_err(&i2c->dev, "i2c read16 fail reg:0x%x error %d\n", reg, ret);
}
mutex_unlock(&usbpd_data->i2c_mutex);
return ret;
}
int s2mm005_write_byte(const struct i2c_client *i2c, u16 reg, u8 *val, u16 size)
{
int ret, i2c_retry; u8 buf[258] = {0,};
struct i2c_msg msg[1];
struct s2mm005_data *usbpd_data = i2c_get_clientdata(i2c);
#if defined(CONFIG_USB_HW_PARAM)
struct otg_notify *o_notify = get_otg_notify();
#endif
if (size > 256) {
pr_err("I2C error, over the size %d", size);
return -EIO;
}
mutex_lock(&usbpd_data->i2c_mutex);
i2c_retry = 0;
msg[0].addr = i2c->addr;
msg[0].flags = 0;
msg[0].len = size+2;
msg[0].buf = buf;
buf[0] = (reg & 0xFF00) >> 8;
buf[1] = (reg & 0xFF);
memcpy(&buf[2], val, size);
do {
ret = i2c_transfer(i2c->adapter, msg, 1);
} while (ret < 0 && i2c_retry++ < 5);
if (ret < 0) {
#if defined(CONFIG_USB_HW_PARAM)
if (o_notify)
inc_hw_param(o_notify, USB_CCIC_I2C_ERROR_COUNT);
#endif
dev_err(&i2c->dev, "i2c write fail reg:0x%x error %d\n", reg, ret);
}
mutex_unlock(&usbpd_data->i2c_mutex);
return ret;
}
int s2mm005_read_byte_16(const struct i2c_client *i2c, u16 reg, u8 *val)
{
int ret; u8 wbuf[2], rbuf;
struct i2c_msg msg[2];
struct s2mm005_data *usbpd_data = i2c_get_clientdata(i2c);
#if defined(CONFIG_USB_HW_PARAM)
struct otg_notify *o_notify = get_otg_notify();
#endif
mutex_lock(&usbpd_data->i2c_mutex);
msg[0].addr = i2c->addr;
msg[0].flags = i2c->flags;
msg[0].len = 2;
msg[0].buf = wbuf;
msg[1].addr = i2c->addr;
msg[1].flags = I2C_M_RD;
msg[1].len = 1;
msg[1].buf = &rbuf;
wbuf[0] = (reg & 0xFF00) >> 8;
wbuf[1] = (reg & 0xFF);
ret = i2c_transfer(i2c->adapter, msg, 2);
if (ret < 0) {
#if defined(CONFIG_USB_HW_PARAM)
if (o_notify)
inc_hw_param(o_notify, USB_CCIC_I2C_ERROR_COUNT);
#endif
dev_err(&i2c->dev, "i2c read16 fail reg(0x%x), error %d\n", reg, ret);
}
mutex_unlock(&usbpd_data->i2c_mutex);
*val = rbuf;
return rbuf;
}
int s2mm005_write_byte_16(const struct i2c_client *i2c, u16 reg, u8 val)
{
int ret = 0; u8 wbuf[3];
struct i2c_msg msg[1];
struct s2mm005_data *usbpd_data = i2c_get_clientdata(i2c);
#if defined(CONFIG_USB_HW_PARAM)
struct otg_notify *o_notify = get_otg_notify();
#endif
mutex_lock(&usbpd_data->i2c_mutex);
msg[0].addr = i2c->addr;
msg[0].flags = 0;
msg[0].len = 3;
msg[0].buf = wbuf;
wbuf[0] = (reg & 0xFF00) >> 8;
wbuf[1] = (reg & 0xFF);
wbuf[2] = (val & 0xFF);
ret = i2c_transfer(i2c->adapter, msg, 1);
if (ret < 0) {
#if defined(CONFIG_USB_HW_PARAM)
if (o_notify)
inc_hw_param(o_notify, USB_CCIC_I2C_ERROR_COUNT);
#endif
dev_err(&i2c->dev, "i2c write fail reg(0x%x:%x), error %d\n", reg, val, ret);
}
mutex_unlock(&usbpd_data->i2c_mutex);
return ret;
}
void s2mm005_int_clear(struct s2mm005_data *usbpd_data)
{
struct i2c_client *i2c = usbpd_data->i2c;
pr_info("%s : -- clear clear --\n", __func__);
s2mm005_write_byte_16(i2c, 0x10, 0x1);
}
void s2mm005_reset(struct s2mm005_data *usbpd_data)
{
struct i2c_client *i2c = usbpd_data->i2c;
uint16_t REG_ADD;
u8 W_DATA[5];
u8 R_DATA[1];
int i;
pr_info("%s\n", __func__);
/* for Wake up*/
for (i = 0; i < 5; i++) {
R_DATA[0] = 0x00;
REG_ADD = 0x8;
s2mm005_read_byte(i2c, REG_ADD, R_DATA, 1); //dummy read
}
udelay(10);
W_DATA[0] = 0x02;
W_DATA[1] = 0x01;
W_DATA[2] = 0x1C;
W_DATA[3] = 0x10;
W_DATA[4] = 0x01;
REG_ADD = 0x10;
s2mm005_write_byte(i2c, REG_ADD, &W_DATA[0], 5);
/* reset stable time */
msleep(100);
}
void s2mm005_reset_enable(struct s2mm005_data *usbpd_data)
{
struct i2c_client *i2c = usbpd_data->i2c;
uint16_t REG_ADD;
u8 W_DATA[5];
pr_info("%s\n", __func__);
W_DATA[0] = 0x02;
W_DATA[1] = 0x01;
W_DATA[2] = 0x5C;
W_DATA[3] = 0x10;
W_DATA[4] = 0x01;
REG_ADD = 0x10;
s2mm005_write_byte(i2c, REG_ADD, &W_DATA[0], 5);
}
void s2mm005_system_reset(struct s2mm005_data *usbpd_data)
{
struct i2c_client *i2c = usbpd_data->i2c;
u8 W_DATA[6];
u8 R_DATA[6];
W_DATA[0] = 0x2;
W_DATA[1] = 0x20; //word write
W_DATA[2] = 0x64;
W_DATA[3] = 0x10;
s2mm005_write_byte(i2c, 0x10, &W_DATA[0], 4);
s2mm005_read_byte(i2c, 0x14, &R_DATA[0], 2);
/* SYSTEM RESET */
W_DATA[0] = 0x02;
W_DATA[1] = 0x02;
W_DATA[2] = 0x68;
W_DATA[3] = 0x10;
W_DATA[4] = R_DATA[0];
W_DATA[5] = R_DATA[1];
s2mm005_write_byte(i2c, 0x10, &W_DATA[0], 6);
}
void s2mm005_hard_reset(struct s2mm005_data *usbpd_data)
{
struct i2c_client *i2c = usbpd_data->i2c;
struct device *i2c_dev = i2c->dev.parent->parent;
struct pinctrl *i2c_pinctrl;
i2c_lock_adapter(i2c->adapter);
i2c_pinctrl = devm_pinctrl_get_select(i2c_dev, "hard_reset");
if (IS_ERR(i2c_pinctrl))
pr_err("could not set reset pins\n");
pr_info("hard_reset: %04d %1d %01d\n", __LINE__, gpio_get_value(usbpd_data->s2mm005_sda), gpio_get_value(usbpd_data->s2mm005_scl));
usleep_range(10 * 1000, 10 * 1000);
i2c_pinctrl = devm_pinctrl_get_select(i2c_dev, "default");
if (IS_ERR(i2c_pinctrl))
pr_err("could not set default pins\n");
usleep_range(8 * 1000, 8 * 1000);
i2c_unlock_adapter(i2c->adapter);
printk("hard_reset: %04d %1d %01d\n", __LINE__, gpio_get_value(usbpd_data->s2mm005_sda), gpio_get_value(usbpd_data->s2mm005_scl));
}
void s2mm005_sram_reset(struct s2mm005_data *usbpd_data)
{
struct i2c_client *i2c = usbpd_data->i2c;
uint16_t REG_ADD;
u8 W_DATA[5];
pr_info("%s\n", __func__);
/* boot control reset OM HIGH */
W_DATA[0] = 0x02;
W_DATA[1] = 0x01;
W_DATA[2] = 0x1C;
W_DATA[3] = 0x10;
W_DATA[4] = 0x08;
REG_ADD = 0x10;
s2mm005_write_byte(i2c, REG_ADD, &W_DATA[0], 5);
}
void s2mm005_reconnect(struct s2mm005_data *usbpd_data)
{
struct i2c_client *i2c = usbpd_data->i2c;
uint16_t REG_ADD;
u8 W_DATA[3];
pr_info("%s\n", __func__);
W_DATA[0] = 0x03;
W_DATA[1] = 0x02;
W_DATA[2] = 0x00;
REG_ADD = 0x10;
s2mm005_write_byte(i2c, REG_ADD, &W_DATA[0], 3);
}
void s2mm005_manual_JIGON(struct s2mm005_data *usbpd_data, int mode)
{
struct i2c_client *i2c = usbpd_data->i2c;
uint16_t REG_ADD;
u8 W_DATA[5];
u8 R_DATA[1];
int i;
pr_info("usb: %s mode=%s (fw=0x%x)\n", __func__, mode ? "High":"Low", usbpd_data->firm_ver[2]);
/* for Wake up*/
for (i = 0; i < 5; i++) {
R_DATA[0] = 0x00;
REG_ADD = 0x8;
s2mm005_read_byte(i2c, REG_ADD, R_DATA, 1); //dummy read
}
udelay(10);
W_DATA[0] = 0x0F;
if (mode)
W_DATA[1] = 0x5; // JIGON High
else
W_DATA[1] = 0x4; // JIGON Low
REG_ADD = 0x10;
s2mm005_write_byte(i2c, REG_ADD, &W_DATA[0], 2);
}
void s2mm005_manual_LPM(struct s2mm005_data *usbpd_data, int cmd)
{
struct i2c_client *i2c = usbpd_data->i2c;
uint16_t REG_ADD;
u8 W_DATA[2];
u8 R_DATA[1];
int i;
pr_info("usb: %s cmd=0x%x (fw=0x%x)\n", __func__, cmd, usbpd_data->firm_ver[2]);
/* for Wake up*/
for (i = 0; i < 5; i++) {
R_DATA[0] = 0x00;
REG_ADD = 0x8;
s2mm005_read_byte(i2c, REG_ADD, R_DATA, 1); //dummy read
}
udelay(10);
W_DATA[0] = 0x0F;
W_DATA[1] = cmd;
REG_ADD = 0x10;
s2mm005_write_byte(i2c, REG_ADD, &W_DATA[0], 2);
}
void s2mm005_control_option_command(struct s2mm005_data *usbpd_data, int cmd)
{
struct i2c_client *i2c = usbpd_data->i2c;
uint16_t REG_ADD;
u8 W_DATA[2];
u8 R_DATA[1];
int i;
printk("usb: %s cmd=0x%x (fw=0x%x)\n", __func__, cmd, usbpd_data->firm_ver[2]);
/* for Wake up*/
for (i = 0; i < 5; i++) {
R_DATA[0] = 0x00;
REG_ADD = 0x8;
s2mm005_read_byte(i2c, REG_ADD, R_DATA, 1); //dummy read
}
udelay(10);
// 0x81 : Vconn control option command ON
// 0x82 : Vconn control option command OFF
// 0x83 : Water Detect option command ON
// 0x84 : Water Detect option command OFF
#if defined(CONFIG_SEC_FACTORY)
if ((cmd&0xF) == 0x3)
usbpd_data->fac_water_enable = 1;
else if ((cmd&0xF) == 0x4)
usbpd_data->fac_water_enable = 0;
#endif
REG_ADD = 0x10;
W_DATA[0] = 0x03;
W_DATA[1] = 0x80 | (cmd&0xF);
s2mm005_write_byte(i2c, REG_ADD, &W_DATA[0], 2);
}
#if defined(CONFIG_DUAL_ROLE_USB_INTF)
static void s2mm005_new_toggling_control(struct s2mm005_data *usbpd_data, u8 mode)
{
struct i2c_client *i2c = usbpd_data->i2c;
uint16_t REG_ADD;
u8 W_DATA[2];
pr_info("%s, mode=0x%x\n", __func__, mode);
W_DATA[0] = 0x03;
W_DATA[1] = mode; // 0x12 : detach, 0x13 : SRC, 0x14 : SNK
REG_ADD = 0x10;
s2mm005_write_byte(i2c, REG_ADD, &W_DATA[0], 2);
}
static void s2mm005_toggling_control(struct s2mm005_data *usbpd_data, u8 mode)
{
struct i2c_client *i2c = usbpd_data->i2c;
uint16_t REG_ADD;
u8 W_DATA[5];
pr_info("%s, mode=0x%x\n", __func__, mode);
W_DATA[0] = 0x02;
W_DATA[1] = 0x01;
W_DATA[2] = 0x00;
W_DATA[3] = 0x50;
W_DATA[4] = mode; // 0x1 : SRC, 0x2 : SNK, 0x3: DRP
REG_ADD = 0x10;
s2mm005_write_byte(i2c, REG_ADD, &W_DATA[0], 5);
}
#endif
int s2mm005_fw_ver_check(void *data)
{
struct s2mm005_data *usbpd_data = data;
struct s2mm005_version chip_swver, hwver;
if ((usbpd_data->firm_ver[1] == 0xFF && usbpd_data->firm_ver[2] == 0xFF)
|| (usbpd_data->firm_ver[1] == 0x00 && usbpd_data->firm_ver[2] == 0x00)) {
s2mm005_get_chip_hwversion(usbpd_data, &hwver);
pr_err("%s CHIP HWversion %2x %2x %2x %2x\n", __func__,
hwver.main[2], hwver.main[1], hwver.main[0], hwver.boot);
s2mm005_get_chip_swversion(usbpd_data, &chip_swver);
pr_err("%s CHIP SWversion %2x %2x %2x %2x\n", __func__,
chip_swver.main[2], chip_swver.main[1], chip_swver.main[0], chip_swver.boot);
if ((chip_swver.main[0] == 0xFF && chip_swver.main[1] == 0xFF)
|| (chip_swver.main[0] == 0x00 && chip_swver.main[1] == 0x00)) {
pr_err("%s Invalid FW version\n", __func__);
return CCIC_FW_VERSION_INVALID;
}
store_ccic_version(&hwver.main[0], &chip_swver.main[0], &chip_swver.boot);
usbpd_data->firm_ver[0] = chip_swver.main[2];
usbpd_data->firm_ver[1] = chip_swver.main[1];
usbpd_data->firm_ver[2] = chip_swver.main[0];
usbpd_data->firm_ver[3] = chip_swver.boot;
}
return 0;
}
void s2mm005_set_upsm_mode(void)
{
struct s2mm005_data *usbpd_data;
u8 W_DATA[2];
usbpd_data = ccic_core_get_drvdata();
if (!usbpd_data)
return;
W_DATA[0] = 0x3;
W_DATA[1] = 0x40;
s2mm005_write_byte(usbpd_data->i2c, 0x10, &W_DATA[0], 2);
pr_info("%s : current status is MPSM!\n", __func__);
}
#if defined(CONFIG_CCIC_NOTIFIER)
static int s2mm005_sysfs_get_local_prop(struct _ccic_data_t *pccic_data,
enum ccic_sysfs_property prop,
char *buf)
{
int retval = -ENODEV;
struct s2mm005_version chip_swver;
struct s2mm005_version fw_swver;
u8 W_DATA[4];
u8 REG_ADD;
u8 R_DATA;
int i;
u8 val = 0;
struct s2mm005_data *usbpd_data = (struct s2mm005_data *)pccic_data->drv_data;
if (!usbpd_data) {
pr_info("%s : usbpd_data is null : request prop = %d\n", __func__, prop);
return -ENODEV;
}
switch (prop) {
case CCIC_SYSFS_PROP_CUR_VERSION:
s2mm005_get_chip_swversion(usbpd_data, &chip_swver);
pr_err("%s CHIP SWversion %2x %2x %2x %2x\n", __func__,
chip_swver.main[2], chip_swver.main[1], chip_swver.main[0], chip_swver.boot);
usbpd_data->firm_ver[0] = chip_swver.main[2];
usbpd_data->firm_ver[1] = chip_swver.main[1];
usbpd_data->firm_ver[2] = chip_swver.main[0];
usbpd_data->firm_ver[3] = chip_swver.boot;
retval = sprintf(buf, "%02X %02X %02X %02X\n",
usbpd_data->firm_ver[0], usbpd_data->firm_ver[1],
usbpd_data->firm_ver[2], usbpd_data->firm_ver[3]);
break;
case CCIC_SYSFS_PROP_SRC_VERSION:
s2mm005_get_fw_version(usbpd_data->s2mm005_fw_product_id,
&fw_swver, usbpd_data->firm_ver[3], usbpd_data->hw_rev);
return sprintf(buf, "%02X %02X %02X %02X\n",
fw_swver.main[2], fw_swver.main[1], fw_swver.main[0], fw_swver.boot);
break;
case CCIC_SYSFS_PROP_LPM_MODE:
retval = sprintf(buf, "%d\n", usbpd_data->manual_lpm_mode);
break;
case CCIC_SYSFS_PROP_STATE:
retval = sprintf(buf, "%d\n", usbpd_data->pd_state);
break;
case CCIC_SYSFS_PROP_RID:
retval = sprintf(buf, "%d\n", usbpd_data->cur_rid);
break;
#if defined(CONFIG_SEC_FACTORY)
case CCIC_SYSFS_PROP_BOOTING_DRY:
pr_info("%s booting_run_dry=%d\n", __func__, usbpd_data->fac_booting_dry_check);
retval = sprintf(buf, "%d\n", (usbpd_data->fac_booting_dry_check));
break;
#endif
case CCIC_SYSFS_PROP_FW_UPDATE_STATUS:
s2mm005_read_byte_flash(usbpd_data->i2c, FLASH_STATUS_0x24, &val, 1);
pr_err("%s flash mode: %s\n", __func__, flashmode_to_string(val));
retval = sprintf(buf, "%s\n", flashmode_to_string(val));
break;
case CCIC_SYSFS_PROP_FW_WATER:
pr_info("%s water=%d, run_dry=%d\n", __func__,
usbpd_data->water_det, usbpd_data->run_dry);
retval = sprintf(buf, "%d\n", (usbpd_data->water_det | !usbpd_data->run_dry));
break;
case CCIC_SYSFS_PROP_ACC_DEVICE_VERSION:
pr_info("%s 0x%04x\n", __func__, usbpd_data->Device_Version);
retval = sprintf(buf, "%04x\n", usbpd_data->Device_Version);
break;
case CCIC_SYSFS_PROP_CONTROL_GPIO:
/* for Wake up*/
for (i = 0; i < 5; i++) {
R_DATA = 0x00;
REG_ADD = 0x8;
s2mm005_read_byte(usbpd_data->i2c, REG_ADD, &R_DATA, 1); //dummy read
}
udelay(10);
W_DATA[0] = 0x2;
W_DATA[1] = 0x10;
W_DATA[2] = 0x84;
W_DATA[3] = 0x10;
s2mm005_write_byte(usbpd_data->i2c, 0x10, &W_DATA[0], 4);
s2mm005_read_byte(usbpd_data->i2c, 0x14, &R_DATA, 1);
pr_err("%s SBU1 status = %2x , SBU2 status = %2x \n", __func__,
(R_DATA & 0x10) >> 4, (R_DATA & 0x20) >> 5);
retval = sprintf(buf, "%d %d\n", (R_DATA & 0x10) >> 4, (R_DATA & 0x20) >> 5);
break;
case CCIC_SYSFS_PROP_USBPD_IDS:
retval = sprintf(buf, "%04x:%04x\n",
le16_to_cpu(usbpd_data->Vendor_ID),
le16_to_cpu(usbpd_data->Product_ID));
pr_info("usb: %s CCIC_SYSFS_USBPD_IDS : %s",
__func__, buf);
break;
case CCIC_SYSFS_PROP_USBPD_TYPE:
retval = sprintf(buf, "%d\n", usbpd_data->acc_type);
pr_info("usb: %s CCIC_SYSFS_USBPD_TYPE : %d",
__func__, usbpd_data->acc_type);
break;
default:
pr_info("%s prop read not supported prop (%d)\n", __func__, prop);
retval = -ENODATA;
break;
}
pr_info("%s : usbpd_data(%p),request prop = %d\n", __func__, usbpd_data, prop);
return retval;
}
static int ccic_firmware_update_built_in(struct s2mm005_data *usbpd_data)
{
struct s2mm005_version chip_swver, fw_swver;
s2mm005_get_chip_swversion(usbpd_data, &chip_swver);
pr_err("%s CHIP SWversion %2x %2x %2x %2x - before\n", __func__,
chip_swver.main[2], chip_swver.main[1], chip_swver.main[0], chip_swver.boot);
s2mm005_get_fw_version(usbpd_data->s2mm005_fw_product_id,
&fw_swver, chip_swver.boot, usbpd_data->hw_rev);
pr_err("%s SRC SWversion:%2x,%2x,%2x,%2x\n", __func__,
fw_swver.main[2], fw_swver.main[1], fw_swver.main[0], fw_swver.boot);
pr_err("%s: FW UPDATE boot:%01d hw_rev:%02d\n", __func__, chip_swver.boot, usbpd_data->hw_rev);
if (chip_swver.main[0] == fw_swver.main[0]) {
pr_err("%s: FW version is same. Stop FW update. src:%2x chip:%2x\n",
__func__, chip_swver.main[0], fw_swver.main[0]);
goto done;
}
s2mm005_flash_fw(usbpd_data, chip_swver.boot);
done:
return 0;
}
static int ccic_firmware_update_ums(struct s2mm005_data *usbpd_data)
{
unsigned char *fw_data;
struct s2mm005_fw *fw_hd;
struct file *fp;
mm_segment_t old_fs;
long fw_size, nread;
int error = 0;
if (!usbpd_data) {
pr_err("%s usbpd_data is null!!\n", __func__);
return -ENODEV;
}
old_fs = get_fs();
set_fs(KERNEL_DS);
fp = filp_open(CCIC_DEFAULT_UMS_FW, O_RDONLY, S_IRUSR);
if (IS_ERR(fp)) {
pr_err("%s: failed to open %s.\n", __func__,
CCIC_DEFAULT_UMS_FW);
error = -ENOENT;
goto open_err;
}
fw_size = fp->f_path.dentry->d_inode->i_size;
if (0 < fw_size) {
fw_data = kzalloc(fw_size, GFP_KERNEL);
nread = vfs_read(fp, (char __user *)fw_data, fw_size, &fp->f_pos);
pr_info("%s: start, file path %s, size %ld Bytes\n",
__func__, CCIC_DEFAULT_UMS_FW, fw_size);
filp_close(fp, NULL);
if (nread != fw_size) {
pr_err("%s: failed to read firmware file, nread %ld Bytes\n",
__func__, nread);
error = -EIO;
} else {
fw_hd = (struct s2mm005_fw *)fw_data;
pr_info("CCIC FW ver - cur:%02X %02X %02X %02X / bin:%02X %02X %02X %02X\n",
usbpd_data->firm_ver[0], usbpd_data->firm_ver[1], usbpd_data->firm_ver[2], usbpd_data->firm_ver[3],
fw_hd->boot, fw_hd->main[0], fw_hd->main[1], fw_hd->main[2]);
if (fw_hd->boot == usbpd_data->firm_ver[3]) {
if (s2mm005_flash_fw(usbpd_data, FLASH_WRITE_UMS) >= 0)
goto done;
} else {
pr_err("error : Didn't match to CCIC FW firmware version\n");
error = -EINVAL;
}
}
if (error < 0)
pr_err("%s: failed update firmware\n", __func__);
done:
kfree(fw_data);
}
open_err:
set_fs(old_fs);
return error;
}
static ssize_t s2mm005_sysfs_set_prop(struct _ccic_data_t *pccic_data,
enum ccic_sysfs_property prop,
const char *buf, size_t size)
{
ssize_t retval = size;
int mode = 0;
struct s2mm005_data *usbpd_data = (struct s2mm005_data *)pccic_data->drv_data;
struct s2mm005_version version;
int rv;
const char *ss_vdd;
struct regulator *vdd085_usb;
u8 W_DATA[2];
u8 REG_ADD;
u8 R_DATA;
int i;
struct device_node *np = NULL;
if (!usbpd_data) {
pr_err("%s : usbpd_data is null : request prop = %d\n", __func__, prop);
return -ENODEV;
}
switch (prop) {
case CCIC_SYSFS_PROP_LPM_MODE:
rv = sscanf(buf, "%d", &mode);
pr_info("usb: %s mode=%d\n", __func__, mode);
switch (mode) {
case 0:
/* Disable Low Power Mode for App (SW JIGON Disable) */
s2mm005_manual_JIGON(usbpd_data, 0);
usbpd_data->manual_lpm_mode = 0;
break;
case 1:
/* Enable Low Power Mode for App (SW JIGON Enable) */
s2mm005_manual_JIGON(usbpd_data, 1);
usbpd_data->manual_lpm_mode = 1;
break;
case 2:
/* SW JIGON Enable */
s2mm005_manual_JIGON(usbpd_data, 1);
usbpd_data->manual_lpm_mode = 1;
break;
default:
/* SW JIGON Disable */
s2mm005_manual_JIGON(usbpd_data, 0);
usbpd_data->manual_lpm_mode = 0;
break;
}
break;
case CCIC_SYSFS_PROP_CTRL_OPTION:
rv = sscanf(buf, "%d", &mode);
pr_info("usb: %s mode=%d\n", __func__, mode);
s2mm005_control_option_command(usbpd_data, mode);
break;
case CCIC_SYSFS_PROP_FW_UPDATE:
rv = sscanf(buf, "%d", &mode);
pr_info("%s mode=%d\n", __func__, mode);
s2mm005_get_chip_swversion(usbpd_data, &version);
pr_err("%s CHIP SWversion %2x %2x %2x %2x - before\n", __func__,
version.main[2], version.main[1], version.main[0], version.boot);
/* Factory cmd for firmware update
* argument represent what is source of firmware like below.
*
* 0 : [BUILT_IN] Getting firmware from source.
* 1 : [UMS] Getting firmware from sd card.
*/
switch (mode) {
case BUILT_IN:
rv = ccic_firmware_update_built_in(usbpd_data);
break;
case UMS:
rv = ccic_firmware_update_ums(usbpd_data);
break;
default:
pr_err("%s: Not support command[%d]\n",
__func__, mode);
break;
}
s2mm005_get_chip_swversion(usbpd_data, &version);
pr_err("%s CHIP SWversion %2x %2x %2x %2x - after\n", __func__,
version.main[2], version.main[1], version.main[0], version.boot);
break;
case CCIC_SYSFS_PROP_DEX_FAN_UVDM:
rv = sscanf(buf, "%d", &mode);
pr_info("%s mode=%d\n", __func__, mode);
send_attention_message(usbpd_data, mode);
break;
case CCIC_SYSFS_PROP_CONTROL_GPIO:
rv = sscanf(buf, "%d", &mode);
pr_info("usb: %s mode=%d\n", __func__, mode);
np = of_find_compatible_node(NULL, NULL, "samsung,usb-notifier");
if (!np) {
pr_err("%s: failed to get the battery device node\n", __func__);
return 0;
} else {
if (of_property_read_string(np, "hs-regulator", (char const **)&ss_vdd) < 0) {
pr_err("%s - get ss_vdd error\n", __func__);
}
vdd085_usb = regulator_get(NULL, ss_vdd);
if (IS_ERR(vdd085_usb) || vdd085_usb == NULL) {
pr_err("%s - vdd085_usb regulator_get fail\n", __func__);
return 0;
}
}
/* for Wake up*/
for (i = 0; i < 5; i++) {
R_DATA = 0x00;
REG_ADD = 0x8;
s2mm005_read_byte(usbpd_data->i2c, REG_ADD, &R_DATA, 1); //dummy read
}
udelay(10);
switch (mode) {
case 0:
if (!regulator_is_enabled(vdd085_usb)) {
rv = regulator_enable(vdd085_usb);
if (rv) {
pr_err("%s - enable vdd085_usb ldo enable failed, ret=%d\n",
__func__, rv);
regulator_put(vdd085_usb);
return 0;
}
}
regulator_put(vdd085_usb);
/* SBU1/SBU2 set as open-drain status*/
// SBU1/2 Open command ON
REG_ADD = 0x10;
W_DATA[0] = 0x03;
W_DATA[1] = 0x85;
s2mm005_write_byte(usbpd_data->i2c, REG_ADD, &W_DATA[0], 2);
break;
case 1:
/* SBU1/SBU2 set as default status */
// SBU1/2 Open command OFF
REG_ADD = 0x10;
W_DATA[0] = 0x03;
W_DATA[1] = 0x86;
s2mm005_write_byte(usbpd_data->i2c, REG_ADD, &W_DATA[0], 2);
if (regulator_is_enabled(vdd085_usb)) {
rv = regulator_disable(vdd085_usb);
if (rv) {
pr_err("%s - enable vdd085_usb ldo enable failed, ret=%d\n",
__func__, rv);
regulator_put(vdd085_usb);
return 0;
}
}
regulator_put(vdd085_usb);
break;
default:
break;
}
break;
default:
pr_info("%s prop write not supported prop (%d)\n", __func__, prop);
retval = -ENODATA;
return retval;
}
return size;
}
static int s2mm005_sysfs_is_writeable(struct _ccic_data_t *pccic_data,
enum ccic_sysfs_property prop)
{
switch (prop) {
case CCIC_SYSFS_PROP_LPM_MODE:
case CCIC_SYSFS_PROP_CTRL_OPTION:
case CCIC_SYSFS_PROP_FW_UPDATE:
case CCIC_SYSFS_PROP_DEX_FAN_UVDM:
case CCIC_SYSFS_PROP_CONTROL_GPIO:
return 1;
default:
return 0;
}
}
#endif
#if defined(CONFIG_DUAL_ROLE_USB_INTF)
void s2mm005_rprd_mode_change(struct s2mm005_data *usbpd_data, u8 mode)
{
pr_info("%s, mode=0x%x\n", __func__, mode);
switch (mode) {
case TYPE_C_ATTACH_DFP: // SRC
s2mm005_new_toggling_control(usbpd_data, 0x12);
msleep(1000);
s2mm005_new_toggling_control(usbpd_data, 0x13);
break;
case TYPE_C_ATTACH_UFP: // SNK
s2mm005_new_toggling_control(usbpd_data, 0x12);
msleep(1000);
s2mm005_new_toggling_control(usbpd_data, 0x14);
break;
case TYPE_C_ATTACH_DRP: // DRP
s2mm005_toggling_control(usbpd_data, TYPE_C_ATTACH_DRP);
break;
};
}
#endif
static irqreturn_t s2mm005_usbpd_irq_thread(int irq, void *data)
{
struct s2mm005_data *usbpd_data = data;
struct i2c_client *i2c = usbpd_data->i2c;
int irq_gpio_status[2];
u8 plug_attach_done;
u8 pdic_attach = 0;
uint32_t *pPRT_MSG = NULL;
MSG_IRQ_STATUS_Type MSG_IRQ_State;
dev_info(&i2c->dev, "%d times\n", ++usbpd_data->wq_times);
if (usbpd_data->ccic_check_at_booting) {
usbpd_data->ccic_check_at_booting = 0;
cancel_delayed_work_sync(&usbpd_data->ccic_init_work);
}
// Function State
irq_gpio_status[0] = gpio_get_value(usbpd_data->irq_gpio);
dev_info(&i2c->dev, "IRQ0:%02d\n", irq_gpio_status[0]);
wake_lock_timeout(&usbpd_data->wlock, HZ);
if (s2mm005_fw_ver_check(usbpd_data) == CCIC_FW_VERSION_INVALID) {
goto ver_err;
}
// Send attach event
process_cc_attach(usbpd_data, &plug_attach_done);
if (usbpd_data->water_det || !usbpd_data->run_dry || !usbpd_data->booting_run_dry) {
process_cc_water_det(usbpd_data);
goto water;
}
// Get staus interrupt register
process_cc_get_int_status(usbpd_data, pPRT_MSG, &MSG_IRQ_State);
// pd irq processing
process_pd(usbpd_data, plug_attach_done, &pdic_attach, &MSG_IRQ_State);
// RID processing
process_cc_rid(usbpd_data);
ver_err:
water:
/* ========================================== */
// s2mm005_int_clear(usbpd_data);
irq_gpio_status[1] = gpio_get_value(usbpd_data->irq_gpio);
dev_info(&i2c->dev, "IRQ1:%02d", irq_gpio_status[1]);
return IRQ_HANDLED;
}
#if defined(CONFIG_OF)
static int of_s2mm005_usbpd_dt(struct device *dev,
struct s2mm005_data *usbpd_data)
{
struct device_node *np = dev->of_node;
int ret;
usbpd_data->irq_gpio = of_get_named_gpio(np, "usbpd,usbpd_int", 0);
usbpd_data->redriver_en = of_get_named_gpio(np, "usbpd,redriver_en", 0);
usbpd_data->s2mm005_om = of_get_named_gpio(np, "usbpd,s2mm005_om", 0);
usbpd_data->s2mm005_sda = of_get_named_gpio(np, "usbpd,s2mm005_sda", 0);
usbpd_data->s2mm005_scl = of_get_named_gpio(np, "usbpd,s2mm005_scl", 0);
if (of_property_read_u32(np, "usbpd,s2mm005_fw_product_id", &usbpd_data->s2mm005_fw_product_id)) {
usbpd_data->s2mm005_fw_product_id = 0x01;
}
np = of_find_all_nodes(NULL);
ret = of_property_read_u32(np, "model_info-hw_rev", &usbpd_data->hw_rev);
if (ret) {
pr_info("%s: model_info-hw_rev is Empty\n", __func__);
usbpd_data->hw_rev = 0;
}
dev_err(dev, "hw_rev:%02d usbpd_irq = %d redriver_en = %d s2mm005_om = %d\n"
"s2mm005_sda = %d, s2mm005_scl = %d, fw_product_id=0x%02X\n",
usbpd_data->hw_rev,
usbpd_data->irq_gpio, usbpd_data->redriver_en, usbpd_data->s2mm005_om,
usbpd_data->s2mm005_sda, usbpd_data->s2mm005_scl,
usbpd_data->s2mm005_fw_product_id);
return 0;
}
#endif /* CONFIG_OF */
void ccic_state_check_work(struct work_struct *wk)
{
struct s2mm005_data *usbpd_data =
container_of(wk, struct s2mm005_data, ccic_init_work.work);
pr_info("%s - check state=%d\n", __func__, usbpd_data->ccic_check_at_booting);
if (usbpd_data->ccic_check_at_booting) {
usbpd_data->ccic_check_at_booting = 0;
s2mm005_usbpd_irq_thread(usbpd_data->irq, usbpd_data);
}
}
static int pdic_handle_usb_external_notifier_notification(struct notifier_block *nb,
unsigned long action, void *data)
{
struct s2mm005_data *usbpd_data = ccic_core_get_drvdata();
int ret = 0;
int enable = *(int *)data;
pr_info("%s : action=%lu , enable=%d\n", __func__, action, enable);
switch (action) {
case EXTERNAL_NOTIFY_HOSTBLOCK_PRE:
if (enable) {
set_enable_alternate_mode(ALTERNATE_MODE_STOP);
if (usbpd_data->dp_is_connect)
dp_detach(usbpd_data);
} else {
if (usbpd_data->dp_is_connect)
dp_detach(usbpd_data);
}
break;
case EXTERNAL_NOTIFY_HOSTBLOCK_POST:
if (enable) {
;
} else {
set_enable_alternate_mode(ALTERNATE_MODE_START);
}
break;
default:
break;
}
return ret;
return 0;
}
static void delayed_external_notifier_init(struct work_struct *work)
{
int ret = 0;
static int retry_count = 1;
int max_retry_count = 5;
struct s2mm005_data *usbpd_data = ccic_core_get_drvdata();
pr_info("%s : %d = times!\n", __func__, retry_count);
// Register ccic handler to ccic notifier block list
ret = usb_external_notify_register(&usbpd_data->usb_external_notifier_nb,
pdic_handle_usb_external_notifier_notification, EXTERNAL_NOTIFY_DEV_PDIC);
if (ret < 0) {
pr_err("Manager notifier init time is %d.\n", retry_count);
if (retry_count++ != max_retry_count)
schedule_delayed_work(&usbpd_data->usb_external_notifier_register_work, msecs_to_jiffies(2000));
else
pr_err("fail to init external notifier\n");
} else
pr_info("%s : external notifier register done!\n", __func__);
}
static int s2mm005_usbpd_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct i2c_adapter *adapter = to_i2c_adapter(i2c->dev.parent);
struct s2mm005_data *usbpd_data;
int ret = 0;
#ifdef CONFIG_CCIC_LPM_ENABLE
u8 check[8] = {0,};
#endif
uint16_t REG_ADD;
uint8_t MSG_BUF[32] = {0,};
SINK_VAR_SUPPLY_Typedef *pSINK_MSG;
MSG_HEADER_Typedef *pMSG_HEADER;
#if defined(CONFIG_CCIC_NOTIFIER)
pccic_data_t pccic_data;
pccic_sysfs_property_t pccic_sysfs_prop;
#endif
#if defined(CONFIG_SEC_FACTORY)
LP_STATE_Type Lp_DATA;
#endif
uint32_t *MSG_DATA;
uint8_t cnt;
u8 W_DATA[8];
u8 R_DATA[4];
u8 temp, ftrim;
struct s2mm005_version chip_swver, fw_swver, hwver;
#if defined(CONFIG_DUAL_ROLE_USB_INTF)
struct dual_role_phy_desc *desc;
struct dual_role_phy_instance *dual_role;
#endif
#if defined(CONFIG_USB_HOST_NOTIFY)
struct otg_notify *o_notify = get_otg_notify();
#endif
pr_info("%s\n", __func__);
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
dev_err(&i2c->dev, "i2c functionality check error\n");
return -EIO;
}
usbpd_data = devm_kzalloc(&i2c->dev, sizeof(struct s2mm005_data), GFP_KERNEL);
if (!usbpd_data) {
return -ENOMEM;
}
#if defined(CONFIG_OF)
if (i2c->dev.of_node)
of_s2mm005_usbpd_dt(&i2c->dev, usbpd_data);
else
dev_err(&i2c->dev, "not found ccic dt! ret:%d\n", ret);
#endif
ret = gpio_request(usbpd_data->irq_gpio, "s2mm005_irq");
if (ret)
goto err_free_irq_gpio;
if (gpio_is_valid(usbpd_data->redriver_en)) {
ret = gpio_request(usbpd_data->redriver_en, "s2mm005_redriver_en");
if (ret)
goto err_free_redriver_gpio;
/* TODO REMOVE redriver always enable, Add sleep/resume */
ret = gpio_direction_output(usbpd_data->redriver_en, 1);
if (ret) {
dev_err(&i2c->dev, "Unable to set input gpio direction, error %d\n", ret);
goto err_free_redriver_gpio;
}
}
gpio_direction_input(usbpd_data->irq_gpio);
usbpd_data->irq = gpio_to_irq(usbpd_data->irq_gpio);
dev_info(&i2c->dev, "%s:IRQ NUM %d\n", __func__, usbpd_data->irq);
usbpd_data->dev = &i2c->dev;
usbpd_data->i2c = i2c;
i2c_set_clientdata(i2c, usbpd_data);
//dev_set_drvdata(ccic_device, usbpd_data);
device_init_wakeup(usbpd_data->dev, 1);
pd_noti.pusbpd = usbpd_data;
mutex_init(&usbpd_data->i2c_mutex);
/* Init */
usbpd_data->p_prev_rid = -1;
usbpd_data->prev_rid = -1;
usbpd_data->cur_rid = -1;
usbpd_data->is_dr_swap = 0;
usbpd_data->is_pr_swap = 0;
usbpd_data->pd_state = 0;
usbpd_data->func_state = 0;
usbpd_data->data_role = 0;
usbpd_data->is_host = 0;
usbpd_data->is_client = 0;
usbpd_data->manual_lpm_mode = 0;
usbpd_data->water_det = 0;
usbpd_data->run_dry = 1;
usbpd_data->booting_run_dry = 1;
#if defined(CONFIG_DUAL_ROLE_USB_INTF)
usbpd_data->try_state_change = 0;
#endif
#if defined(CONFIG_SEC_FACTORY)
usbpd_data->fac_water_enable = 0;
#endif
wake_lock_init(&usbpd_data->wlock, WAKE_LOCK_SUSPEND,
"s2mm005-intr");
#if defined(CONFIG_CCIC_NOTIFIER)
/* Create a work queue for the ccic irq thread */
usbpd_data->ccic_wq
= create_singlethread_workqueue("ccic_irq_event");
if (!usbpd_data->ccic_wq) {
pr_err("%s failed to create work queue\n", __func__);
ret = -ENOMEM;
goto err_free_redriver_gpio;
}
#endif
#if defined(CONFIG_CCIC_NOTIFIER)
pccic_data = kzalloc(sizeof(ccic_data_t), GFP_KERNEL);
pccic_sysfs_prop = kzalloc(sizeof(ccic_sysfs_property_t), GFP_KERNEL);
pccic_sysfs_prop->get_property = s2mm005_sysfs_get_local_prop;
pccic_sysfs_prop->set_property = s2mm005_sysfs_set_prop;
pccic_sysfs_prop->property_is_writeable = s2mm005_sysfs_is_writeable;
pccic_sysfs_prop->properties = s2mm005_sysfs_properties;
pccic_sysfs_prop->num_properties = ARRAY_SIZE(s2mm005_sysfs_properties);
pccic_data->ccic_syfs_prop = pccic_sysfs_prop;
pccic_data->drv_data = usbpd_data;
pccic_data->name = "s2mm005";
pccic_data->set_enable_alternate_mode = set_enable_alternate_mode;
ccic_core_register_chip(pccic_data);
dev_err(&i2c->dev, "registered ccic core \n");
#endif
dev_err(&i2c->dev, "probed, irq %d\n", usbpd_data->irq_gpio);
for (cnt = 0; cnt < 32; cnt++) {
MSG_BUF[cnt] = 0;
}
REG_ADD = REG_TX_SINK_CAPA_MSG;
ret = s2mm005_read_byte(i2c, REG_ADD, MSG_BUF, 32);
if (ret < 0) {
s2mm005_hard_reset(usbpd_data);
msleep(1000);
ret = s2mm005_read_byte(i2c, REG_ADD, MSG_BUF, 32);
if (ret < 0) {
/* to check wrong ccic chipsets, It will be removed after PRA */
panic("Intentional Panic - ccic i2c error\n");
// dev_err(&i2c->dev, "%s has i2c read error.\n", __func__);
// goto err_init_irq;
}
}
s2mm005_get_chip_hwversion(usbpd_data, &hwver);
pr_err("%s CHIP HWversion %2x %2x %2x %2x\n", __func__,
hwver.main[2], hwver.main[1], hwver.main[0], hwver.boot);
pr_err("%s CHIP HWversion2 %2x %2x %2x %2x\n", __func__,
hwver.ver2[3], hwver.ver2[2], hwver.ver2[1], hwver.ver2[0]);
if (hwver.boot <= 2) {
W_DATA[0] = 0x02; W_DATA[1] = 0x40; W_DATA[2] = 0x04; W_DATA[3] = 0x11;
s2mm005_write_byte(i2c, 0x10, &W_DATA[0], 4);
s2mm005_read_byte(i2c, 0x14, &R_DATA[0], 4);
pr_err("ftrim:%02X %02X %02X %02X\n", R_DATA[0], R_DATA[1], R_DATA[2], R_DATA[3]);
ftrim = ((R_DATA[1] & 0xF8) >> 3) - 2;
temp = R_DATA[1] & 0x7;
R_DATA[1] = (ftrim << 3) + temp;
pr_err("ftrim:%02X %02X %02X %02X\n", R_DATA[0], R_DATA[1], R_DATA[2], R_DATA[3]);
W_DATA[0] = 0x02; W_DATA[1] = 0x04; W_DATA[2] = 0x04; W_DATA[3] = 0x11;
W_DATA[4] = R_DATA[0]; W_DATA[5] = R_DATA[1]; W_DATA[6] = R_DATA[2]; W_DATA[7] = R_DATA[3];
s2mm005_write_byte(i2c, 0x10, &W_DATA[0], 8);
W_DATA[0] = 0x02; W_DATA[1] = 0x40; W_DATA[2] = 0x04; W_DATA[3] = 0x11;
s2mm005_write_byte(i2c, 0x10, &W_DATA[0], 4);
s2mm005_read_byte(i2c, 0x14, &R_DATA[0], 4);
pr_err("ftrim:%02X %02X %02X %02X\n", R_DATA[0], R_DATA[1], R_DATA[2], R_DATA[3]);
}
s2mm005_get_chip_swversion(usbpd_data, &chip_swver);
pr_err("%s CHIP SWversion %2x %2x %2x %2x\n", __func__,
chip_swver.main[2], chip_swver.main[1], chip_swver.main[0], chip_swver.boot);
pr_err("%s CHIP SWversion2 %2x %2x %2x %2x\n", __func__,
chip_swver.ver2[3], chip_swver.ver2[2], chip_swver.ver2[1], chip_swver.ver2[0]);
s2mm005_get_fw_version(usbpd_data->s2mm005_fw_product_id,
&fw_swver, chip_swver.boot, usbpd_data->hw_rev);
pr_err("%s SRC SWversion: %2x,%2x,%2x,%2x\n", __func__,
fw_swver.main[2], fw_swver.main[1], fw_swver.main[0], fw_swver.boot);
pr_err("%s: FW UPDATE boot:%01d hw_rev:%02d\n", __func__,
chip_swver.boot, usbpd_data->hw_rev);
usbpd_data->fw_product_id = fw_swver.main[2];
#if defined(CONFIG_SEC_FACTORY)
s2mm005_read_byte(i2c, 0x60, Lp_DATA.BYTE, 4);
pr_err("%s: WATER reg:0x%02X BOOTING_RUN_DRY=%d\n", __func__,
Lp_DATA.BYTE[0], Lp_DATA.BITS.BOOTING_RUN_DRY);
usbpd_data->fac_booting_dry_check = Lp_DATA.BITS.BOOTING_RUN_DRY;
#endif
if (chip_swver.boot == 0x7) {
#ifdef CONFIG_SEC_FACTORY
if ((chip_swver.main[0] != fw_swver.main[0]) /* main version */
|| (chip_swver.main[1] != fw_swver.main[1]) /* sub version */
|| (chip_swver.main[2] != fw_swver.main[2])) { /* product id */
if (s2mm005_flash_fw(usbpd_data, chip_swver.boot) < 0) {
pr_err("%s: s2mm005_flash_fw 1st fail, try again \n", __func__);
if (s2mm005_flash_fw(usbpd_data, chip_swver.boot) < 0) {
pr_err("%s: s2mm005_flash_fw 2st fail, panic \n", __func__);
panic("infinite write fail!\n");
}
}
}
#else
if ((chip_swver.main[0] < fw_swver.main[0])
|| ((chip_swver.main[0] == fw_swver.main[0]) && (chip_swver.main[1] < fw_swver.main[1]))
|| (chip_swver.main[2] != fw_swver.main[2]))
s2mm005_flash_fw(usbpd_data, chip_swver.boot);
else if ((((chip_swver.main[2] == 0xff) && (chip_swver.main[1] == 0xa5)) || chip_swver.main[2] == 0x00) &&
fw_swver.main[2] != 0x0) //extra case, factory or old version (for dream)
s2mm005_flash_fw(usbpd_data, chip_swver.boot);
#endif
s2mm005_get_chip_swversion(usbpd_data, &chip_swver);
pr_err("%s CHIP SWversion %2x %2x %2x %2x\n", __func__,
chip_swver.main[2], chip_swver.main[1], chip_swver.main[0], chip_swver.boot);
pr_err("%s CHIP SWversion2 %2x %2x %2x %2x\n", __func__,
chip_swver.ver2[3], chip_swver.ver2[2], chip_swver.ver2[1], chip_swver.ver2[0]);
}
store_ccic_version(&hwver.main[0], &chip_swver.main[0], &chip_swver.boot);
usbpd_data->firm_ver[0] = chip_swver.main[2];
usbpd_data->firm_ver[1] = chip_swver.main[1];
usbpd_data->firm_ver[2] = chip_swver.main[0];
usbpd_data->firm_ver[3] = chip_swver.boot;
MSG_DATA = (uint32_t *)&MSG_BUF[0];
dev_info(&i2c->dev, "--- Read Data on TX_SNK_CAPA_MSG(0x220)\n\r");
for (cnt = 0; cnt < 8; cnt++) {
dev_info(&i2c->dev, " 0x%08X\n\r", MSG_DATA[cnt]);
}
pMSG_HEADER = (MSG_HEADER_Typedef *)&MSG_BUF[0];
pMSG_HEADER->BITS.Number_of_obj += 1;
pSINK_MSG = (SINK_VAR_SUPPLY_Typedef *)&MSG_BUF[8];
pSINK_MSG->DATA = 0x8F019032; // 5V~12V, 500mA
dev_info(&i2c->dev, "--- Write DATA\n\r");
for (cnt = 0; cnt < 8; cnt++) {
dev_info(&i2c->dev, " 0x%08X\n\r", MSG_DATA[cnt]);
}
/* default value is written by CCIC FW. If you need others, overwrite it.*/
//s2mm005_write_byte(i2c, REG_ADD, &MSG_BUF[0], 32);
for (cnt = 0; cnt < 32; cnt++) {
MSG_BUF[cnt] = 0;
}
for (cnt = 0; cnt < 8; cnt++) {
dev_info(&i2c->dev, " 0x%08X\n\r", MSG_DATA[cnt]);
}
ret = s2mm005_read_byte(i2c, REG_ADD, MSG_BUF, 32);
dev_info(&i2c->dev, "--- Read 2 new Data on TX_SNK_CAPA_MSG(0x220)\n\r");
for (cnt = 0; cnt < 8; cnt++) {
dev_info(&i2c->dev, " 0x%08X\n\r", MSG_DATA[cnt]);
}
#ifdef CONFIG_CCIC_LPM_ENABLE
pr_err("LPM_ENABLE\n");
check[0] = 0x0F;
check[1] = 0x06;
s2mm005_write_byte(i2c, 0x10, &check[0], 2);
#endif
#if defined(CONFIG_DUAL_ROLE_USB_INTF)
desc =
devm_kzalloc(&i2c->dev,
sizeof(struct dual_role_phy_desc), GFP_KERNEL);
if (!desc) {
pr_err("unable to allocate dual role descriptor\n");
goto err_init_irq;
}
desc->name = "otg_default";
desc->supported_modes = DUAL_ROLE_SUPPORTED_MODES_DFP_AND_UFP;
desc->get_property = dual_role_get_local_prop;
desc->set_property = dual_role_set_prop;
desc->properties = fusb_drp_properties;
desc->num_properties = ARRAY_SIZE(fusb_drp_properties);
desc->property_is_writeable = dual_role_is_writeable;
dual_role =
devm_dual_role_instance_register(&i2c->dev, desc);
dual_role->drv_data = usbpd_data;
usbpd_data->dual_role = dual_role;
usbpd_data->desc = desc;
init_completion(&usbpd_data->reverse_completion);
usbpd_data->power_role = DUAL_ROLE_PROP_PR_NONE;
#if defined(CONFIG_USB_HOST_NOTIFY)
send_otg_notify(o_notify, NOTIFY_EVENT_POWER_SOURCE, 0);
#endif
INIT_DELAYED_WORK(&usbpd_data->role_swap_work, role_swap_check);
#endif
#if defined(CONFIG_CCIC_ALTERNATE_MODE)
usbpd_data->alternate_state = 0;
usbpd_data->acc_type = 0;
usbpd_data->dp_is_connect = 0;
usbpd_data->dp_hs_connect = 0;
usbpd_data->dp_selected_pin = 0;
usbpd_data->pin_assignment = 0;
usbpd_data->is_samsung_accessory_enter_mode = 0;
usbpd_data->Vendor_ID = 0;
usbpd_data->Product_ID = 0;
usbpd_data->Device_Version = 0;
ccic_register_switch_device(1);
INIT_DELAYED_WORK(&usbpd_data->acc_detach_work, acc_detach_check);
init_waitqueue_head(&usbpd_data->host_turn_on_wait_q);
set_host_turn_on_event(0);
usbpd_data->host_turn_on_wait_time = 2;
#endif
fp_select_pdo = s2mm005_select_pdo;
usbpd_data->ccic_check_at_booting = 1;
INIT_DELAYED_WORK(&usbpd_data->ccic_init_work, ccic_state_check_work);
schedule_delayed_work(&usbpd_data->ccic_init_work, msecs_to_jiffies(200));
ret = request_threaded_irq(usbpd_data->irq, NULL, s2mm005_usbpd_irq_thread,
(IRQF_TRIGGER_FALLING | IRQF_NO_SUSPEND | IRQF_ONESHOT), "s2mm005-usbpd", usbpd_data);
if (ret) {
dev_err(&i2c->dev, "Failed to request IRQ %d, error %d\n", usbpd_data->irq, ret);
goto err_init_irq;
}
#if defined(CONFIG_BATTERY_SAMSUNG)
if (usbpd_data->s2mm005_fw_product_id == PRODUCT_NUM_DREAM) {
u8 W_CHG_INFO[3] = {0,};
W_CHG_INFO[0] = 0x0f;
W_CHG_INFO[1] = 0x0c;
if (lpcharge)
W_CHG_INFO[2] = 0x1; // lpcharge
else
W_CHG_INFO[2] = 0x0; // normal
s2mm005_write_byte(usbpd_data->i2c, 0x10, &W_CHG_INFO[0], 3); // send info to ccic
}
#endif
INIT_DELAYED_WORK(&usbpd_data->usb_external_notifier_register_work,
delayed_external_notifier_init);
// Register ccic handler to ccic notifier block list
ret = usb_external_notify_register(&usbpd_data->usb_external_notifier_nb,
pdic_handle_usb_external_notifier_notification, EXTERNAL_NOTIFY_DEV_PDIC);
if (ret < 0)
schedule_delayed_work(&usbpd_data->usb_external_notifier_register_work, msecs_to_jiffies(2000));
else
pr_info("%s : external notifier register done!\n", __func__);
s2mm005_int_clear(usbpd_data);
return ret;
err_init_irq:
if (usbpd_data->irq) {
free_irq(usbpd_data->irq, usbpd_data);
usbpd_data->irq = 0;
}
err_free_redriver_gpio:
gpio_free(usbpd_data->redriver_en);
err_free_irq_gpio:
wake_lock_destroy(&usbpd_data->wlock);
gpio_free(usbpd_data->irq_gpio);
return ret;
}
static int s2mm005_usbpd_remove(struct i2c_client *i2c)
{
struct s2mm005_data *usbpd_data = ccic_core_get_drvdata();
process_cc_detach(usbpd_data);
#if defined(CONFIG_DUAL_ROLE_USB_INTF)
devm_dual_role_instance_unregister(usbpd_data->dev, usbpd_data->dual_role);
devm_kfree(usbpd_data->dev, usbpd_data->desc);
#endif
if (usbpd_data->irq) {
free_irq(usbpd_data->irq, usbpd_data);
usbpd_data->irq = 0;
}
if (usbpd_data->i2c) {
disable_irq_wake(usbpd_data->i2c->irq);
free_irq(usbpd_data->i2c->irq, usbpd_data);
mutex_destroy(&usbpd_data->i2c_mutex);
i2c_set_clientdata(usbpd_data->i2c, NULL);
}
wake_lock_destroy(&usbpd_data->wlock);
return 0;
}
static void s2mm005_usbpd_shutdown(struct i2c_client *i2c)
{
struct s2mm005_data *usbpd_data = i2c_get_clientdata(i2c);
#if defined(CONFIG_CCIC_ALTERNATE_MODE)
struct device_node *np;
int gpio_dp_sw_oe;
#endif
disable_irq(usbpd_data->irq);
if ((usbpd_data->cur_rid != RID_523K) &&
(usbpd_data->cur_rid != RID_619K) &&
(!usbpd_data->manual_lpm_mode)) {
pr_info("%s: pd_state=%d, water=%d, dry=%d\n", __func__,
usbpd_data->pd_state, usbpd_data->water_det, usbpd_data->run_dry);
if (usbpd_data->water_det) {
s2mm005_hard_reset(usbpd_data);
} else {
if (usbpd_data->pd_state) {
#if defined(CONFIG_CCIC_ALTERNATE_MODE)
if (usbpd_data->dp_is_connect) {
pr_info("aux_sw_oe pin set to high\n");
np = of_find_node_by_name(NULL, "displayport");
gpio_dp_sw_oe = of_get_named_gpio(np, "dp,aux_sw_oe", 0);
gpio_direction_output(gpio_dp_sw_oe, 1);
}
#endif
s2mm005_manual_LPM(usbpd_data, 0xB);
mdelay(110);
}
s2mm005_reset(usbpd_data);
}
}
}
#if defined(CONFIG_PM)
static int s2mm005_suspend(struct device *dev)
{
struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
struct s2mm005_data *usbpd_data = i2c_get_clientdata(i2c);
#if !defined(CONFIG_SAMSUNG_PRODUCT_SHIP)
pr_info("%s:%s\n", USBPD005_DEV_NAME, __func__);
#endif /* CONFIG_SAMSUNG_PRODUCT_SHIP */
if (device_may_wakeup(dev))
enable_irq_wake(usbpd_data->irq);
disable_irq(usbpd_data->irq);
return 0;
}
static int s2mm005_resume(struct device *dev)
{
struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
struct s2mm005_data *usbpd_data = i2c_get_clientdata(i2c);
#if !defined(CONFIG_SAMSUNG_PRODUCT_SHIP)
pr_info("%s:%s\n", USBPD005_DEV_NAME, __func__);
#endif /* CONFIG_SAMSUNG_PRODUCT_SHIP */
if (device_may_wakeup(dev))
disable_irq_wake(usbpd_data->irq);
enable_irq(usbpd_data->irq);
return 0;
}
#else
#define s2mm005_suspend NULL
#define s2mm005_resume NULL
#endif /* CONFIG_PM */
static const struct i2c_device_id s2mm005_usbpd_id[] = {
{ USBPD005_DEV_NAME, 0 },
{}
};
MODULE_DEVICE_TABLE(i2c, s2mm005_usbpd_id);
#if defined(CONFIG_OF)
static struct of_device_id s2mm005_i2c_dt_ids[] = {
{ .compatible = "sec-s2mm005,i2c" },
{ }
};
#endif /* CONFIG_OF */
#if defined(CONFIG_PM)
const struct dev_pm_ops s2mm005_pm = {
.suspend = s2mm005_suspend,
.resume = s2mm005_resume,
};
#endif /* CONFIG_PM */
static struct i2c_driver s2mm005_usbpd_driver = {
.driver = {
.name = USBPD005_DEV_NAME,
#if defined(CONFIG_PM)
.pm = &s2mm005_pm,
#endif /* CONFIG_PM */
#if defined(CONFIG_OF)
.of_match_table = s2mm005_i2c_dt_ids,
#endif /* CONFIG_OF */
},
.probe = s2mm005_usbpd_probe,
//.remove = __devexit_p(s2mm005_usbpd_remove),
.remove = s2mm005_usbpd_remove,
.shutdown = s2mm005_usbpd_shutdown,
.id_table = s2mm005_usbpd_id,
};
static int __init s2mm005_usbpd_init(void)
{
return i2c_add_driver(&s2mm005_usbpd_driver);
}
module_init(s2mm005_usbpd_init);
static void __exit s2mm005_usbpd_exit(void)
{
i2c_del_driver(&s2mm005_usbpd_driver);
}
module_exit(s2mm005_usbpd_exit);
MODULE_DESCRIPTION("s2mm005 USB PD driver");
MODULE_LICENSE("GPL");