blob: 122b105510412fda4e91661fb01a2376ab786dee [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_ALTERNATE_MODE)
#include <linux/ccic/ccic_alternate.h>
#endif
extern struct device *ccic_device;
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);
int ccic_misc_init(void);
void ccic_misc_exit(void);
////////////////////////////////////////////////////////////////////////////////
//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,
//};
////////////////////////////////////////////////////////////////////////////////
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);
printk("%s\n",__func__);
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];
printk("%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");
printk("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];
printk("%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];
printk("%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) || defined(CONFIG_TYPEC))
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];
if(!ccic_device)
return;
usbpd_data = dev_get_drvdata(ccic_device);
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 upsm! \n",__func__);
}
#if (defined(CONFIG_DUAL_ROLE_USB_INTF) || defined(CONFIG_TYPEC))
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->s2mm005_i2c_err < 0)
goto i2cErr;
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);
i2cErr:
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,water_detect_support", &usbpd_data->water_detect_support)) {
usbpd_data->water_detect_support = 1;
}
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 = dev_get_drvdata(ccic_device);
int ret = 0;
int is_src = 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) {
is_src = usbpd_data->func_state & (0x1 << 25) ? 1 : 0;
if (is_src)
s2mm005_set_upsm_mode();
} else {
set_enable_alternate_mode(ALTERNATE_MODE_START);
}
break;
default:
break;
}
return ret;
}
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 = dev_get_drvdata(ccic_device);
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_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) {
dev_err(&i2c->dev, "Failed to allocate driver data\n");
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);
return -ENODEV;
}
#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);
if (ccic_device == NULL) ccic_notifier_init(); // temp
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
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 == 0x8) {
#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;
INIT_DELAYED_WORK(&usbpd_data->role_swap_work, role_swap_check);
#elif defined(CONFIG_TYPEC)
usbpd_data->typec_cap.revision = USB_TYPEC_REV_1_2;
usbpd_data->typec_cap.pd_revision = 0x300;
usbpd_data->typec_cap.prefer_role = TYPEC_NO_PREFERRED_ROLE;
usbpd_data->typec_cap.pr_set = s2mm005_pr_set;
usbpd_data->typec_cap.dr_set = s2mm005_dr_set;
usbpd_data->typec_cap.port_type_set = s2mm005_port_type_set;
usbpd_data->typec_cap.type = TYPEC_PORT_DRP;
usbpd_data->typec_power_role = TYPEC_SINK;
usbpd_data->typec_data_role = TYPEC_DEVICE;
usbpd_data->typec_try_state_change = TRY_ROLE_SWAP_NONE;
usbpd_data->port = typec_register_port(usbpd_data->dev, &usbpd_data->typec_cap);
if (IS_ERR(usbpd_data->port))
pr_err("%s : unable to register typec_register_port\n", __func__);
else
pr_err("%s : success typec_register_port port=%pK\n", __func__, usbpd_data->port);
usbpd_data->partner = NULL;
init_completion(&usbpd_data->typec_reverse_completion);
INIT_DELAYED_WORK(&usbpd_data->typec_role_swap_work, typec_role_swap_check);
#endif
usbpd_data->pd_support = false;
#if defined(CONFIG_USB_HOST_NOTIFY)
send_otg_notify(o_notify, NOTIFY_EVENT_POWER_SOURCE, 0);
#endif
#if defined(CONFIG_CCIC_ALTERNATE_MODE)
init_completion(&usbpd_data->uvdm_out_wait);
init_completion(&usbpd_data->uvdm_longpacket_in_wait);
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;
ret = ccic_misc_init();
if (ret) {
dev_err(&i2c->dev, "ccic misc register is failed, error %d\n", ret);
goto err_init_irq;
}
#endif
s2mm005_int_clear(usbpd_data);
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 = dev_get_drvdata(ccic_device);
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);
#elif defined(CONFIG_TYPEC)
typec_unregister_port(usbpd_data->port);
#endif
sysfs_remove_group(&ccic_device->kobj, &ccic_sysfs_group);
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);
ccic_misc_exit();
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);
msleep(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");