android_kernel_samsung_hero.../drivers/ccic/s2mm003.c
2016-08-17 16:41:52 +08:00

651 lines
21 KiB
C

/*
* driver/../s2mm003.c - S2MM003 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/s2mm003.h>
static int s2mm003_read_byte(const struct i2c_client *i2c, u8 reg)
{
int ret; u8 wbuf, rbuf;
struct i2c_msg msg[2];
msg[0].addr = i2c->addr;
msg[0].flags = i2c->flags;
msg[0].len = 1;
msg[0].buf = &wbuf;
msg[1].addr = i2c->addr;
msg[1].flags = I2C_M_RD;
msg[1].len = 2;
msg[1].buf = &rbuf;
wbuf = (reg & 0xFF);
ret = i2c_transfer(i2c->adapter, msg, 2);
if (ret < 0)
dev_err(&i2c->dev, "i2c reading fail reg(0x%x), error %d\n",
reg, ret);
return rbuf;
}
static int s2mm003_read_byte_16(const struct i2c_client *i2c, u16 reg, u8 *val)
{
int ret; u8 wbuf[2], rbuf;
struct i2c_msg msg[2];
msg[0].addr = 0x43;
msg[0].flags = i2c->flags;
msg[0].len = 2;
msg[0].buf = wbuf;
msg[1].addr = 0x43;
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)
dev_err(&i2c->dev, "i2c read16 fail reg(0x%x), error %d\n",
reg, ret);
*val = rbuf;
return rbuf;
}
static int s2mm003_write_byte(const struct i2c_client *i2c, u8 reg, u8 val)
{
int ret = 0; u8 wbuf[2];
struct i2c_msg msg[1];
msg[0].addr = i2c->addr;
msg[0].flags = 0;
msg[0].len = 2;
msg[0].buf = wbuf;
wbuf[0] = (reg & 0xFF);
wbuf[1] = (val & 0xFF);
ret = i2c_transfer(i2c->adapter, msg, 1);
if (ret < 0)
dev_err(&i2c->dev, "i2c write fail reg(0x%x:%x), error %d\n",
reg, val, ret);
return ret;
}
static int s2mm003_write_byte_16(const struct i2c_client *i2c, u16 reg, u8 val)
{
int ret = 0; u8 wbuf[3];
struct i2c_msg msg[1] = {
{
.addr = 0x43,
.flags = 0,
.len = 3,
.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)
dev_err(&i2c->dev, "i2c write fail reg(0x%x:%x), error %d\n",
reg, val, ret);
return ret;
}
static void s2mm003_int_clear(struct s2mm003_usbpd_data *usbpd_data)
{
struct i2c_client *i2c = usbpd_data->i2c;
s2mm003_write_byte(i2c, I2C_SYSREG_SET, 0x1);
s2mm003_write_byte(i2c, IRQ_RD_ADDR, 0xFF);
s2mm003_write_byte(i2c, I2C_SYSREG_SET, 0x0);
}
static int s2mm003_indirect_read(struct s2mm003_usbpd_data *usbpd_data, uint8_t address)
{
uint8_t value = 0;
struct i2c_client *i2c = usbpd_data->i2c;
s2mm003_write_byte(i2c, I2C_SYSREG_SET, 0x1);
s2mm003_write_byte(i2c, IRQ_RD_ADDR, address);
s2mm003_write_byte(i2c, I2C_SYSREG_SET, 0x0);
value = s2mm003_read_byte(i2c, IRQ_RD_DATA);
return value;
}
static void s2mm003_indirect_write(struct s2mm003_usbpd_data *usbpd_data, uint8_t address, uint8_t val)
{
struct i2c_client *i2c = usbpd_data->i2c;
s2mm003_write_byte(i2c, I2C_SYSREG_SET, 0x1);
s2mm003_write_byte(i2c, IRQ_WR_ADDR, address);
s2mm003_write_byte(i2c, IRQ_WR_DATA, val);
s2mm003_write_byte(i2c, I2C_SYSREG_SET, 0x0);
}
void s2mm003_src_capacity_information(const struct i2c_client *i2c, uint32_t *RX_SRC_CAPA_MSG, int * choice)
{
uint32_t RdCnt;
uint32_t PDO_cnt;
uint32_t PDO_sel;
MSG_HEADER_Type *MSG_HDR;
SRC_FIXED_SUPPLY_Typedef *MSG_FIXED_SUPPLY;
SRC_VAR_SUPPLY_Typedef *MSG_VAR_SUPPLY;
SRC_BAT_SUPPLY_Typedef *MSG_BAT_SUPPLY;
dev_info(&i2c->dev, "\n\r");
for(RdCnt=0;RdCnt<8;RdCnt++)
{
dev_info(&i2c->dev, "Rd_SRC_CAPA_%d : 0x%X\n\r", RdCnt, RX_SRC_CAPA_MSG[RdCnt]);
}
MSG_HDR = (MSG_HEADER_Type *)&RX_SRC_CAPA_MSG[0];
dev_info(&i2c->dev, "\n\r");
dev_info(&i2c->dev, "=======================================\n\r");
dev_info(&i2c->dev, " MSG Header\n\r");
dev_info(&i2c->dev, " Rsvd_msg_header : %d\n\r",MSG_HDR->Rsvd_msg_header );
dev_info(&i2c->dev, " Number_of_obj : %d\n\r",MSG_HDR->Number_of_obj );
dev_info(&i2c->dev, " Message_ID : %d\n\r",MSG_HDR->Message_ID );
dev_info(&i2c->dev, " Port_Power_Role : %d\n\r",MSG_HDR->Port_Power_Role );
dev_info(&i2c->dev, " Specification_Revision : %d\n\r",MSG_HDR->Specification_Revision );
dev_info(&i2c->dev, " Port_Data_Role : %d\n\r",MSG_HDR->Port_Data_Role );
dev_info(&i2c->dev, " Rsvd2_msg_header : %d\n\r",MSG_HDR->Rsvd2_msg_header );
dev_info(&i2c->dev, " Message_Type : %d\n\r",MSG_HDR->Message_Type );
for(PDO_cnt = 0;PDO_cnt < MSG_HDR->Number_of_obj;PDO_cnt++)
{
PDO_sel = (RX_SRC_CAPA_MSG[PDO_cnt + 1] >> 30) & 0x3;
dev_info(&i2c->dev, " =================\n\r");
dev_info(&i2c->dev, " PDO_Num : %d\n\r", (PDO_cnt + 1));
if(PDO_sel == 0) // *MSG_FIXED_SUPPLY
{
MSG_FIXED_SUPPLY = (SRC_FIXED_SUPPLY_Typedef *)&RX_SRC_CAPA_MSG[PDO_cnt + 1];
if(MSG_FIXED_SUPPLY->Voltage_Unit <= (AVAILABLE_VOLTAGE/UNIT_FOR_VOLTAGE))
*choice = PDO_cnt + 1;
dev_info(&i2c->dev, " PDO_Parameter(FIXED_SUPPLY) : %d\n\r",MSG_FIXED_SUPPLY->PDO_Parameter );
dev_info(&i2c->dev, " Dual_Role_Power : %d\n\r",MSG_FIXED_SUPPLY->Dual_Role_Power );
dev_info(&i2c->dev, " USB_Suspend_Support : %d\n\r",MSG_FIXED_SUPPLY->USB_Suspend_Support );
dev_info(&i2c->dev, " Externally_POW : %d\n\r",MSG_FIXED_SUPPLY->Externally_POW );
dev_info(&i2c->dev, " USB_Comm_Capable : %d\n\r",MSG_FIXED_SUPPLY->USB_Comm_Capable );
dev_info(&i2c->dev, " Data_Role_Swap : %d\n\r",MSG_FIXED_SUPPLY->Data_Role_Swap );
dev_info(&i2c->dev, " Reserved : %d\n\r",MSG_FIXED_SUPPLY->Reserved );
dev_info(&i2c->dev, " Peak_Current : %d\n\r",MSG_FIXED_SUPPLY->Peak_Current );
dev_info(&i2c->dev, " Voltage_Unit : %d\n\r",MSG_FIXED_SUPPLY->Voltage_Unit );
dev_info(&i2c->dev, " Maximum_Current : %d\n\r",MSG_FIXED_SUPPLY->Maximum_Current );
}
else if(PDO_sel == 2) // *MSG_VAR_SUPPLY
{
MSG_VAR_SUPPLY = (SRC_VAR_SUPPLY_Typedef *)&RX_SRC_CAPA_MSG[PDO_cnt + 1];
dev_info(&i2c->dev, " PDO_Parameter(VAR_SUPPLY) : %d\n\r",MSG_VAR_SUPPLY->PDO_Parameter );
dev_info(&i2c->dev, " Maximum_Voltage : %d\n\r",MSG_VAR_SUPPLY->Maximum_Voltage );
dev_info(&i2c->dev, " Minimum_Voltage : %d\n\r",MSG_VAR_SUPPLY->Minimum_Voltage );
dev_info(&i2c->dev, " Maximum_Current : %d\n\r",MSG_VAR_SUPPLY->Maximum_Current );
}
else if(PDO_sel == 1) // *MSG_BAT_SUPPLY
{
MSG_BAT_SUPPLY = (SRC_BAT_SUPPLY_Typedef *)&RX_SRC_CAPA_MSG[PDO_cnt + 1];
dev_info(&i2c->dev, " PDO_Parameter(BAT_SUPPLY) : %d\n\r",MSG_BAT_SUPPLY->PDO_Parameter );
dev_info(&i2c->dev, " Maximum_Voltage : %d\n\r",MSG_BAT_SUPPLY->Maximum_Voltage );
dev_info(&i2c->dev, " Minimum_Voltage : %d\n\r",MSG_BAT_SUPPLY->Minimum_Voltage );
dev_info(&i2c->dev, " Maximum_Allow_Power : %d\n\r",MSG_BAT_SUPPLY->Maximum_Allow_Power );
}
}
dev_info(&i2c->dev, "=======================================\n\r");
dev_info(&i2c->dev, "\n\r");
return;
}
void s2mm003_request_select_type(uint32_t * REQ_MSG , int num)
{
REQUEST_FIXED_SUPPLY_STRUCT_Typedef *REQ_FIXED_SPL;
REQ_FIXED_SPL = (REQUEST_FIXED_SUPPLY_STRUCT_Typedef *)REQ_MSG;
//REQ_FIXED_SPL->Reserved_2 = 0;
REQ_FIXED_SPL->Object_Position = (num & 0x7);
//REQ_FIXED_SPL->GiveBack_Flag = 0; // GiveBack Support set to 1
//REQ_FIXED_SPL->Capa_Mismatch = 0;
//REQ_FIXED_SPL->USB_Comm_Capable = 0;
//REQ_FIXED_SPL->No_USB_Suspend = 0;
//REQ_FIXED_SPL->Reserved_1 = 0; // Set to Zero
REQ_FIXED_SPL->OP_Current = 200; // 10mA
REQ_FIXED_SPL->Maximum_OP_Current = 200; // 10mA
}
static irqreturn_t s2mm003_usbpd_irq_thread(int irq, void *data)
{
struct s2mm003_usbpd_data *usbpd_data = data;
struct i2c_client *i2c = usbpd_data->i2c;
#if !defined(CONFIG_SAMSUNG_S2MM003_SRAM_BUG_WORKAROUND)
unsigned char status[5];
#endif
unsigned char rid, plug_state_monitor;
unsigned char int_status, usbpd_state;
int cc1_valid, cc2_valid, plug_rprd_sel_monitor, plug_attach_done;
int value;
CC_NOTI_ATTACH_TYPEDEF attach_notifier;
CC_NOTI_PD_STATUS_TYPEDEF pd_status_notifier;
CC_NOTI_RID_TYPEDEF rid_notifier;
dev_err(&i2c->dev, "%d times\n", ++wq_times);
#if defined(CONFIG_SAMSUNG_S2MM003_SRAM_BUG_WORKAROUND)
/* Chipset initial value */
rid = s2mm003_indirect_read(usbpd_data, 0x30);
dev_info(&i2c->dev, "RID_GET_MONITOR I2C read: 0x%02X\n", rid);
int_status = s2mm003_indirect_read(usbpd_data, 0x17);
dev_info(&i2c->dev, "INT_STATUS I2C read: 0x%02X\n", int_status);
if((int_status & b_RESET_START) != 0x00)
{
s2mm003_indirect_write(usbpd_data, IND_REG_HOST_CMD_ON, 0x01);
}
usbpd_state = s2mm003_indirect_read(usbpd_data, 0x16);
dev_info(&i2c->dev, "USBPD_STATE I2C read: 0x%02d\n", usbpd_state);
plug_state_monitor = s2mm003_indirect_read(usbpd_data, 0x15);
cc1_valid = S2MM003_REG_MASK(plug_state_monitor, S_CC1_VALID);
cc2_valid = S2MM003_REG_MASK(plug_state_monitor, S_CC2_VALID);
plug_rprd_sel_monitor = S2MM003_REG_MASK(plug_state_monitor, PLUG_RPRD_SEL_MONITOR);
plug_attach_done = S2MM003_REG_MASK(plug_state_monitor, PLUG_ATTACH_DONE);
dev_info(&i2c->dev, "PLUG_STATE_MONITOR I2C read:%x\n"
"CC1:%x CC2:%x rprd:%x attach:%x\n",
plug_state_monitor,
cc1_valid, cc2_valid, plug_rprd_sel_monitor, plug_attach_done);
#else
s2mm003_read_byte_16(i2c, INTERRUPT_STATUS1, &status[0]);
s2mm003_read_byte_16(i2c, INTERRUPT_STATUS2, &status[1]);
s2mm003_read_byte_16(i2c, INTERRUPT_STATUS3, &status[2]);
s2mm003_read_byte_16(i2c, INTERRUPT_STATUS4, &status[3]);
s2mm003_read_byte_16(i2c, INTERRUPT_STATUS5, &status[4]);
dev_err(&i2c->dev, "status : %02X %02X %02X %02X %02X\n",
status[0], status[1], status[2], status[3], status[4]);
s2mm003_read_byte_16(i2c, RID_GET_MONITOR, &rid);
dev_info(&i2c->dev, "RID_GET_MONITOR read: %x\n", rid);
s2mm003_read_byte_16(i2c, PLUG_STATE_MONITOR, &plug_state_monitor);
cc1_valid = S2MM003_REG_MASK(plug_state_monitor, S_CC1_VALID);
cc2_valid = S2MM003_REG_MASK(plug_state_monitor, S_CC2_VALID);
plug_rprd_sel_monitor = S2MM003_REG_MASK(plug_state_monitor, PLUG_RPRD_SEL_MONITOR);
plug_attach_done = S2MM003_REG_MASK(plug_state_monitor, PLUG_ATTACH_DONE);
dev_info(&i2c->dev, "PLUG_STATE_MONITOR read:%x\n"
"CC1:%x CC2:%x rprd:%x attach:%x\n",
plug_state_monitor,
cc1_valid, cc2_valid, plug_rprd_sel_monitor, plug_attach_done);
#endif
/* To confirm whether PD charger is attached or not */
value = s2mm003_indirect_read(usbpd_data, IND_REG_INT_NUMBER);
if( (value & b_PD_FUNC_FLAG) != 0x00) { // Need PD Function State Read
int address = IND_REG_PD_FUNC_STATE; // Function State Number Read
int ret = 0x00;
ret = s2mm003_indirect_read(usbpd_data, address);
/* If it isn't PD charger, return value is 29*/
dev_info(&i2c->dev, " \n\r%s :Function_State = %d\n\r", __func__, ret);
}
if( (value & b_INT_3_FLAG) != 0x00)
{
value = s2mm003_indirect_read(usbpd_data, IND_REG_INT_STATUS_3); // Interrupt State 3 Read
dev_info(&i2c->dev, "INT_State3 = 0x%X\n", value);
if((value & b_RID_Detect_done) != 0x00) // RID Message Detect
{
int ret;
ret = s2mm003_indirect_read(usbpd_data, 0x30); // RID Value Read
dev_info(&i2c->dev, "\n\rRID_Value = 0x%X\n\r", ret);
if (plug_rprd_sel_monitor && plug_attach_done) /* for USB Host Connector */
rid = RID_000K;
cur_rid = rid;
if (cur_rid == prev_rid) {
dev_err(&i2c->dev, "same rid detected, -ignore-\n");
}
p_prev_rid = prev_rid;
prev_rid = cur_rid;
#if defined(CONFIG_CCIC_NOTIFIER)
rid_notifier.src = CCIC_NOTIFY_DEV_CCIC;
rid_notifier.dest = CCIC_NOTIFY_DEV_MUIC;
rid_notifier.id = CCIC_NOTIFY_ID_RID;
rid_notifier.rid = rid;
ccic_notifier_test((CC_NOTI_TYPEDEF*)&rid_notifier);
#endif
}
if((value & b_Msg_SRC_CAP) != 0x00) // Read Source Capability MSG.
{
int choice = 1;
int cnt;
//int ret;
uint32_t ReadMSG[8];
uint8_t *p_MSG_BUF;
s2mm003_indirect_write(usbpd_data, IND_REG_READ_MSG_INDEX, MSG_Idx_RX_SRC_CAPA);// Idx Receive SRC_CAP
p_MSG_BUF = (uint8_t *)ReadMSG;
for(cnt = 0; cnt < 32; cnt++)
{
*(p_MSG_BUF + cnt) = s2mm003_indirect_read(usbpd_data, IND_REG_MSG_ACCESS_BASE+cnt);
}
s2mm003_src_capacity_information(i2c, ReadMSG, &choice);
dev_info(&i2c->dev, "choice : %d\n", choice);
// Select PDO
s2mm003_indirect_write(usbpd_data, IND_REG_READ_MSG_INDEX, MSG_Idx_TX_REQUEST);// Idx Transceiver Request
p_MSG_BUF = (uint8_t *)ReadMSG;
for(cnt = 0; cnt < MSG_BUF_REQUEST_SIZE; cnt++)
{
*(p_MSG_BUF + cnt) = s2mm003_indirect_read(usbpd_data, IND_REG_MSG_ACCESS_BASE+cnt);
}
for(cnt = 0; cnt < MSG_REQUEST_SIZE; cnt++)
{
dev_info(&i2c->dev, "TX_REQ_%d : 0x%X\n\r", cnt, ReadMSG[cnt]);
}
// ReadMSG[1] = 0x2000C864;
//dev_info(&i2c->dev, "Sel PDO Num = %d\n\r",REQ_PDO_NUM);
s2mm003_request_select_type(&ReadMSG[1], choice);
for(cnt = 0; cnt < MSG_BUF_REQUEST_SIZE; cnt++)
{
s2mm003_indirect_write(usbpd_data, IND_REG_MSG_ACCESS_BASE + cnt, *(p_MSG_BUF + cnt));// Idx Transceiver Request
}
p_MSG_BUF = (uint8_t *)ReadMSG;
for(cnt = 0; cnt < MSG_BUF_REQUEST_SIZE; cnt++)
{
*(p_MSG_BUF + cnt) = s2mm003_indirect_read(usbpd_data, IND_REG_MSG_ACCESS_BASE+cnt);
}
for(cnt = 0; cnt < MSG_REQUEST_SIZE; cnt++)
{
dev_info(&i2c->dev, "TX_REQ_%d : 0x%X\n\r", cnt, ReadMSG[cnt]);
}
s2mm003_int_clear(usbpd_data);
#if defined(CONFIG_CCIC_NOTIFIER)
pd_status_notifier.status = plug_attach_done;
pd_status_notifier.id = CCIC_NOTIFY_ID_POWER_STATUS;
pd_status_notifier.src = CCIC_NOTIFY_DEV_PDIC;
pd_status_notifier.dest = CCIC_NOTIFY_DEV_BATTERY;
ccic_notifier_test((CC_NOTI_TYPEDEF*)&pd_status_notifier);
#endif
}
} else {
s2mm003_int_clear(usbpd_data);
}
#if defined(CONFIG_CCIC_NOTIFIER)
attach_notifier.attach = plug_attach_done;
attach_notifier.id = CCIC_NOTIFY_ID_ATTACH;
attach_notifier.src = CCIC_NOTIFY_DEV_CCIC;
attach_notifier.dest = CCIC_NOTIFY_DEV_MUIC;
attach_notifier.rprd = plug_rprd_sel_monitor;
ccic_notifier_test((CC_NOTI_TYPEDEF*)&attach_notifier);
#endif
return IRQ_HANDLED;
}
#if defined(CONFIG_SAMSUNG_S2MM003_SYSFS)
static ssize_t s2mm003_register_show(struct device *dev,
struct device_attribute *att, char *buf)
{
struct s2mm003_usbpd_data *usbpd_data = dev_get_drvdata(dev);
struct i2c_client *i2c = usbpd_data->i2c;
unsigned char status[5];
unsigned char rid, plug_state_monitor;
int irq_gpio_status[2];
int cc1_valid, cc2_valid, plug_rprd_sel_monitor, plug_attach_done;
irq_gpio_status[0] = gpio_get_value(usbpd_data->irq_gpio);
s2mm003_read_byte_16(i2c, INTERRUPT_STATUS1, &status[0]);
s2mm003_read_byte_16(i2c, INTERRUPT_STATUS2, &status[1]);
s2mm003_read_byte_16(i2c, INTERRUPT_STATUS3, &status[2]);
s2mm003_read_byte_16(i2c, INTERRUPT_STATUS4, &status[3]);
s2mm003_read_byte_16(i2c, INTERRUPT_STATUS5, &status[4]);
s2mm003_read_byte_16(i2c, RID_GET_MONITOR, &rid);
s2mm003_read_byte_16(i2c, RID_GET_MONITOR, &rid);
s2mm003_read_byte_16(i2c, PLUG_STATE_MONITOR, &plug_state_monitor);
cc1_valid = S2MM003_REG_MASK(plug_state_monitor, S_CC1_VALID);
cc2_valid = S2MM003_REG_MASK(plug_state_monitor, S_CC2_VALID);
plug_rprd_sel_monitor = S2MM003_REG_MASK(plug_state_monitor, PLUG_RPRD_SEL_MONITOR);
plug_attach_done = S2MM003_REG_MASK(plug_state_monitor, PLUG_ATTACH_DONE);
s2mm003_int_clear(usbpd_data);
irq_gpio_status[1] = gpio_get_value(usbpd_data->irq_gpio);
return snprintf(buf, PAGE_SIZE,
"\n\ninterrupt\n"
"irq_gpio \t\t: %02d %02d\n"
"status(1~5)\t\t: %02X %02X %02X %02X %02X\n"
"CC1:%x CC2:%x rprd:%x attach:%x\n"
"rid : %02x \n",
irq_gpio_status[0], irq_gpio_status[1],
status[0], status[1], status[2], status[3], status[4],
cc1_valid, cc2_valid, plug_rprd_sel_monitor, plug_attach_done,
rid);
}
#endif
#if defined(CONFIG_OF)
static int of_s2mm003_usbpd_dt(struct i2c_client *i2c,
struct s2mm003_usbpd_data *usbpd_data)
{
struct device *dev = &i2c->dev;
struct device_node *np_usbpd = dev->of_node;
if (np_usbpd == NULL) {
dev_err(&i2c->dev, "np NULL\n");
return -EINVAL;
}
usbpd_data->irq_gpio = of_get_named_gpio(np_usbpd, "usbpd,usbpd_int", 0);
if (usbpd_data->irq_gpio < 0) {
dev_err(&i2c->dev, "error reading usbpd_irq = %d\n", usbpd_data->irq_gpio);
usbpd_data->irq_gpio = 0;
}
return 0;
}
#endif /* CONFIG_OF */
static int s2mm003_firmware_update(struct s2mm003_usbpd_data *usbpd_data)
{
const struct firmware *fw_entry;
const unsigned char *p;
int ret, i;
struct i2c_client *i2c = usbpd_data->i2c;
ret = request_firmware(&fw_entry,
FIRMWARE_PATH, usbpd_data->dev);
if (ret < 0)
goto done;
s2mm003_write_byte(i2c, I2C_SRAM_SET, 0x1);
s2mm003_write_byte(i2c, IF_S_CODE_E, 0x4);
for(p = fw_entry->data, i=0; i < fw_entry->size; p++, i++) {
s2mm003_write_byte_16(i2c,(u16)i, *p);
}
s2mm003_write_byte(i2c, I2C_SRAM_SET, 0x0);
s2mm003_write_byte(i2c, IF_S_CODE_E, 0x0);
s2mm003_write_byte(i2c, I2C_SYSREG_SET, 0x1);
s2mm003_write_byte(i2c, USB_PD_RST, 0x80);
s2mm003_write_byte(i2c, I2C_SYSREG_SET, 0x00);
msleep(100);
done:
dev_err(&usbpd_data->i2c->dev, "firmware size: %d, error %d\n",
(int)fw_entry->size, ret);
if (fw_entry)
release_firmware(fw_entry);
return ret;
}
static int s2mm003_usbpd_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct i2c_adapter *adapter = to_i2c_adapter(i2c->dev.parent);
struct s2mm003_usbpd_data *usbpd_data;
int ret = 0;
u8 val;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
dev_err(&i2c->dev, "i2c functionality check error\n");
return -EIO;
}
usbpd_data = kzalloc(sizeof(struct s2mm003_usbpd_data), GFP_KERNEL);
if (!usbpd_data) {
dev_err(&i2c->dev, "Failed to allocate driver data\n");
return -ENOMEM;
}
usbpd_data->i2c = i2c;
#if defined(CONFIG_OF)
ret = of_s2mm003_usbpd_dt(i2c, usbpd_data);
if (ret)
dev_err(&i2c->dev, "not found muic dt! ret:%d\n", ret);
#endif
ret = gpio_request(usbpd_data->irq_gpio, "s2mm003_irq");
if (ret) {
dev_err(&i2c->dev, "Failed to request GPIO pin %d, error %d\n",
(int)usbpd_data->irq_gpio, ret);
goto err_free_usbpd_data;
}
pr_err("%s:%04d", __func__, __LINE__);
ret = gpio_direction_input(usbpd_data->irq_gpio);
if (ret) {
dev_err(&i2c->dev, "Unable to set input gpio direction, error %d\n", ret);
goto err_free_gpio;
}
i2c->irq = gpio_to_irq(usbpd_data->irq_gpio);
ret = enable_irq_wake(i2c->irq);
if (ret) {
dev_err(&i2c->dev, "Failed to enable wakeup source, error %d\n", ret);
goto err_free_gpio;
}
s2mm003_firmware_update(usbpd_data);
ret = request_threaded_irq(i2c->irq, NULL, s2mm003_usbpd_irq_thread,
(IRQF_TRIGGER_FALLING | IRQF_NO_SUSPEND | IRQF_ONESHOT), "s2mm003-usbpd", usbpd_data);
if (ret) {
dev_err(&i2c->dev, "Failed to request IRQ %d, error %d\n", i2c->irq, ret);
goto err_init_irq;
}
i2c_set_clientdata(i2c, usbpd_data);
device_init_wakeup(&i2c->dev, 1);
/* Check Data Opt 1 */
val = s2mm003_indirect_read(usbpd_data, 0x2d);
dev_err(&i2c->dev, "read 0x2d %02X\n", val);
s2mm003_indirect_write(usbpd_data, 0x2d, 0x1);
val = s2mm003_indirect_read(usbpd_data, 0x2d);
dev_err(&i2c->dev, "read 0x2d %02X\n", val);
dev_err(&i2c->dev, "probed, irq %d\n", usbpd_data->irq_gpio);
#if defined(CONFIG_SAMSUNG_S2MM003_SYSFS)
device_create_file(&i2c->dev, &dev_attr_s2mm003_register);
#endif
return ret;
err_init_irq:
if (i2c->irq)
free_irq(i2c->irq, usbpd_data);
err_free_gpio:
gpio_free(usbpd_data->irq_gpio);
err_free_usbpd_data:
kfree(usbpd_data);
return ret;
}
static int s2mm003_usbpd_remove(struct i2c_client *i2c)
{
return 0;
}
static const struct i2c_device_id s2mm003_usbpd_id[] = {
{ USBPD_DEV_NAME, 0 },
{}
};
MODULE_DEVICE_TABLE(i2c, s2mm003_usbpd_id);
#if defined(CONFIG_OF)
static struct of_device_id s2mm003_i2c_dt_ids[] = {
{ .compatible = "sec-s2mm003,i2c" },
{ }
};
#endif /* CONFIG_OF */
static struct i2c_driver s2mm003_usbpd_driver = {
.driver = {
.name = USBPD_DEV_NAME,
#if defined(CONFIG_OF)
.of_match_table = s2mm003_i2c_dt_ids,
#endif /* CONFIG_OF */
},
.probe = s2mm003_usbpd_probe,
//.remove = __devexit_p(s2mm003_usbpd_remove),
.remove = s2mm003_usbpd_remove,
.id_table = s2mm003_usbpd_id,
};
static int __init s2mm003_usbpd_init(void)
{
return i2c_add_driver(&s2mm003_usbpd_driver);
}
module_init(s2mm003_usbpd_init);
static void __exit s2mm003_usbpd_exit(void)
{
i2c_del_driver(&s2mm003_usbpd_driver);
}
module_exit(s2mm003_usbpd_exit);
MODULE_DESCRIPTION("S2MM003 USB PD driver");
MODULE_LICENSE("GPL");