| /* |
| * driver/muic/sm5713-muic.c - SM5713 micro USB switch device driver |
| * |
| * Copyright (C) 2017 SiliconMitus |
| * |
| * 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. |
| * |
| * |
| */ |
| |
| #include <linux/gpio.h> |
| #include <linux/of_gpio.h> |
| #include <linux/i2c.h> |
| #include <linux/interrupt.h> |
| #include <linux/slab.h> |
| #include <linux/platform_device.h> |
| #include <linux/module.h> |
| #include <linux/delay.h> |
| #include <linux/wakelock.h> |
| |
| #ifdef CONFIG_DRV_SAMSUNG |
| #include <linux/sec_class.h> |
| #endif |
| #include <linux/sec_batt.h> |
| #include <linux/sec_ext.h> |
| |
| #if defined(CONFIG_MUIC_NOTIFIER) |
| #include <linux/muic/muic_notifier.h> |
| #endif /* CONFIG_MUIC_NOTIFIER */ |
| |
| #if defined(CONFIG_CCIC_NOTIFIER) |
| #include <linux/usb/typec/pdic_notifier.h> |
| #include <linux/usb_notify.h> |
| #endif /* CONFIG_CCIC_NOTIFIER */ |
| |
| |
| #if defined(CONFIG_VBUS_NOTIFIER) |
| #include <linux/vbus_notifier.h> |
| #endif /* CONFIG_VBUS_NOTIFIER */ |
| |
| #include <linux/muic/muic.h> |
| #include <linux/mfd/sm5713.h> |
| #include <linux/mfd/sm5713-private.h> |
| #include <linux/muic/sm5713-muic.h> |
| |
| #if IS_ENABLED(CONFIG_CP_UART_NOTI) |
| #include <soc/samsung/exynos-modem-ctrl.h> |
| #endif |
| |
| #include "../battery_v2/include/sec_charging_common.h" |
| |
| #define SM5713_MUIC_REG_AFC_CNTL2 0x31 |
| #define SM5713_MUIC_REG_REVID1 0x3e |
| #define SM5713_MUIC_REG_REVID2 0x3f |
| #define SM5713_MUIC_REG_ADC 0x51 |
| #define SM5713_MUIC_REG_CFG1 0x68 |
| #define CTRL_MANUAL_SW_SHIFT 2 |
| |
| #define GPIO_LEVEL_HIGH 1 |
| #define GPIO_LEVEL_LOW 0 |
| |
| #if defined(CONFIG_MUIC_BCD_RESCAN) |
| #define SM5713_MUIC_REG_INT_BCD_RESCAN 0x25 |
| #define SM5713_MUIC_REG_CHG_TYPE 0x50 |
| #endif |
| |
| static struct sm5713_muic_data *static_data; |
| |
| static int com_to_open(struct sm5713_muic_data *muic_data); |
| |
| static void sm5713_muic_handle_attach(struct sm5713_muic_data *muic_data, |
| int new_dev, u8 vbvolt, int irq); |
| static void sm5713_muic_handle_detach(struct sm5713_muic_data *muic_data, |
| int irq); |
| static void sm5713_muic_detect_dev(struct sm5713_muic_data *muic_data, int irq); |
| static int sm5713_muic_get_adc(struct sm5713_muic_data *muic_data); |
| static int switch_to_ap_uart(struct sm5713_muic_data *muic_data, int new_dev); |
| static int switch_to_cp_uart(struct sm5713_muic_data *muic_data, int new_dev); |
| |
| char *SM5713_MUIC_INT_NAME[12] = { |
| "DPDM_OVP", /* 0 */ |
| "VBUS_RID_DETACH", /* 1 */ |
| "AUTOVBUSCHECK", /* 2 */ |
| "RID_DETECT", /* 3 */ |
| "CHGTYPE", /* 4 */ |
| "DCDTIMEOUT", /* 5 */ |
| "AFC_ERROR", /* 6 */ |
| "AFC_STA_CHG", /* 7 */ |
| "MULTI_BYTE", /* 8 */ |
| "VBUS_UPDATE", /* 9 */ |
| "AFC_ACCEPTED", /* 10 */ |
| "AFC_TA_ATTACHED" /* 11 */ |
| }; |
| |
| /* #define DEBUG_MUIC */ |
| |
| #if defined(DEBUG_MUIC) |
| #define MAX_LOG 25 |
| #define READ 0 |
| #define WRITE 1 |
| |
| static u8 sm5713_log_cnt; |
| static u8 sm5713_log[MAX_LOG][3]; |
| |
| static void sm5713_reg_log(u8 reg, u8 value, u8 rw) |
| { |
| sm5713_log[sm5713_log_cnt][0] = reg; |
| sm5713_log[sm5713_log_cnt][1] = value; |
| sm5713_log[sm5713_log_cnt][2] = rw; |
| sm5713_log_cnt++; |
| if (sm5713_log_cnt >= MAX_LOG) |
| sm5713_log_cnt = 0; |
| } |
| |
| static void sm5713_print_reg_log(void) |
| { |
| int i = 0; |
| u8 reg = 0, value = 0, rw = 0; |
| char mesg[256] = ""; |
| |
| for (i = 0; i < MAX_LOG; i++) { |
| reg = sm5713_log[sm5713_log_cnt][0]; |
| value = sm5713_log[sm5713_log_cnt][1]; |
| rw = sm5713_log[sm5713_log_cnt][2]; |
| sm5713_log_cnt++; |
| |
| if (sm5713_log_cnt >= MAX_LOG) |
| sm5713_log_cnt = 0; |
| sprintf(mesg+strlen(mesg), "%x(%x)%x ", reg, value, rw); |
| } |
| pr_info("[%s:%s] %s\n", MUIC_DEV_NAME, __func__, mesg); |
| } |
| |
| void sm5713_read_reg_dump(struct sm5713_muic_data *muic, char *mesg) |
| { |
| u8 val = 0; |
| |
| sm5713_read_reg(muic->i2c, SM5713_MUIC_REG_INTMASK1, &val); |
| sprintf(mesg+strlen(mesg), "IM1:%x ", val); |
| sm5713_read_reg(muic->i2c, SM5713_MUIC_REG_INTMASK2, &val); |
| sprintf(mesg+strlen(mesg), "IM2:%x ", val); |
| sm5713_read_reg(muic->i2c, SM5713_MUIC_REG_CNTL, &val); |
| sprintf(mesg+strlen(mesg), "CTRL:%x ", val); |
| sm5713_read_reg(muic->i2c, SM5713_MUIC_REG_MANUAL_SW, &val); |
| sprintf(mesg+strlen(mesg), "SW:%x ", val); |
| sm5713_read_reg(muic->i2c, SM5713_MUIC_REG_DEVICETYPE1, &val); |
| sprintf(mesg+strlen(mesg), "DT1:%x ", val); |
| sm5713_read_reg(muic->i2c, SM5713_MUIC_REG_DEVICETYPE2, &val); |
| sprintf(mesg+strlen(mesg), "DT2:%x ", val); |
| sm5713_read_reg(muic->i2c, SM5713_MUIC_REG_ADC, &val); |
| sprintf(mesg+strlen(mesg), "ADC:%x ", val); |
| sm5713_read_reg(muic->i2c, SM5713_MUIC_REG_AFCCNTL, &val); |
| sprintf(mesg+strlen(mesg), "AFC_CTRL:%x ", val); |
| sm5713_read_reg(muic->i2c, SM5713_MUIC_REG_AFCTXD, &val); |
| sprintf(mesg+strlen(mesg), "AFC_TXD:%x ", val); |
| sm5713_read_reg(muic->i2c, SM5713_MUIC_REG_VBUS_VOLTAGE1, &val); |
| sprintf(mesg+strlen(mesg), "VOL1:%x ", val); |
| sm5713_read_reg(muic->i2c, SM5713_MUIC_REG_VBUS_VOLTAGE2, &val); |
| sprintf(mesg+strlen(mesg), "VOL2:%x ", val); |
| |
| } |
| void sm5713_print_reg_dump(struct sm5713_muic_data *muic_data) |
| { |
| char mesg[256] = ""; |
| |
| sm5713_read_reg_dump(muic_data, mesg); |
| |
| pr_info("[%s:%s] %s\n", MUIC_DEV_NAME, __func__, mesg); |
| } |
| #else |
| void sm5713_print_reg_dump(struct sm5713_muic_data *muic_data) |
| { |
| } |
| #endif |
| |
| int sm5713_i2c_read_byte(struct i2c_client *client, u8 command) |
| { |
| u8 ret = 0; |
| int retry = 0; |
| |
| sm5713_read_reg(client, command, &ret); |
| |
| while (ret < 0) { |
| pr_info("[%s:%s] reg(0x%x), retrying...\n", |
| MUIC_DEV_NAME, __func__, command); |
| if (retry > 10) { |
| pr_err("[%s:%s] retry failed!!\n", |
| MUIC_DEV_NAME, __func__); |
| break; |
| } |
| msleep(100); |
| sm5713_read_reg(client, command, &ret); |
| retry++; |
| } |
| |
| #ifdef DEBUG_MUIC |
| sm5713_reg_log(command, ret, retry << 1 | READ); |
| #endif |
| return ret; |
| } |
| |
| int sm5713_i2c_write_byte(struct i2c_client *client, |
| u8 command, u8 value) |
| { |
| int ret = 0; |
| int retry = 0; |
| u8 written = 0; |
| |
| ret = sm5713_write_reg(client, command, value); |
| |
| while (ret < 0) { |
| pr_info("[%s:%s] reg(0x%x), retrying...\n", |
| MUIC_DEV_NAME, __func__, command); |
| sm5713_read_reg(client, command, &written); |
| if (written < 0) |
| pr_err("[%s:%s] reg(0x%x)\n", |
| MUIC_DEV_NAME, __func__, command); |
| msleep(100); |
| ret = sm5713_write_reg(client, command, value); |
| retry++; |
| } |
| #ifdef DEBUG_MUIC |
| sm5713_reg_log(command, value, retry << 1 | WRITE); |
| #endif |
| return ret; |
| } |
| static int sm5713_i2c_guaranteed_wbyte(struct i2c_client *client, |
| u8 command, u8 value) |
| { |
| int ret = 0; |
| int retry = 0; |
| int written = 0; |
| |
| ret = sm5713_i2c_write_byte(client, command, value); |
| written = sm5713_i2c_read_byte(client, command); |
| |
| while (written != value) { |
| pr_info("[%s:%s] reg(0x%x): written(0x%x) != value(0x%x)\n", |
| MUIC_DEV_NAME, __func__, command, written, value); |
| if (retry > 10) { |
| pr_err("[%s:%s] retry failed!!\n", MUIC_DEV_NAME, |
| __func__); |
| break; |
| } |
| msleep(100); |
| retry++; |
| ret = sm5713_i2c_write_byte(client, command, value); |
| written = sm5713_i2c_read_byte(client, command); |
| } |
| return ret; |
| } |
| |
| static ssize_t sm5713_muic_show_uart_en(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct sm5713_muic_data *muic_data = dev_get_drvdata(dev); |
| int ret = 0; |
| |
| if (!muic_data->is_rustproof) { |
| pr_info("[%s:%s] UART ENABLE\n", MUIC_DEV_NAME, __func__); |
| ret = sprintf(buf, "1\n"); |
| } else { |
| pr_info("[%s:%s] UART DISABLE\n", MUIC_DEV_NAME, __func__); |
| ret = sprintf(buf, "0\n"); |
| } |
| |
| return ret; |
| } |
| |
| static ssize_t sm5713_muic_set_uart_en(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, size_t count) |
| { |
| struct sm5713_muic_data *muic_data = dev_get_drvdata(dev); |
| |
| if (!strncmp(buf, "1", 1)) |
| muic_data->is_rustproof = false; |
| else if (!strncmp(buf, "0", 1)) |
| muic_data->is_rustproof = true; |
| else |
| pr_info("[%s:%s] invalid value\n", MUIC_DEV_NAME, __func__); |
| |
| pr_info("[%s:%s] uart_en(%d)\n", |
| MUIC_DEV_NAME, __func__, !muic_data->is_rustproof); |
| |
| return count; |
| } |
| |
| #ifndef CONFIG_UART_SWITCH |
| static ssize_t sm5713_muic_show_uart_sel(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct sm5713_muic_data *muic_data = dev_get_drvdata(dev); |
| struct muic_platform_data *pdata = muic_data->pdata; |
| const char *mode = "UNKNOWN\n"; |
| |
| switch (pdata->uart_path) { |
| case MUIC_PATH_UART_AP: |
| mode = "AP\n"; |
| break; |
| case MUIC_PATH_UART_CP: |
| mode = "CP\n"; |
| break; |
| default: |
| break; |
| } |
| |
| pr_info("[%s:%s] uart_sel(%s)\n", MUIC_DEV_NAME, __func__, mode); |
| |
| return sprintf(buf, mode); |
| } |
| |
| static ssize_t sm5713_muic_set_uart_sel(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, size_t count) |
| { |
| struct sm5713_muic_data *muic_data = dev_get_drvdata(dev); |
| struct muic_platform_data *pdata = muic_data->pdata; |
| |
| if (!strncasecmp(buf, "AP", 2)) { |
| pdata->uart_path = MUIC_PATH_UART_AP; |
| switch_to_ap_uart(muic_data, muic_data->attached_dev); |
| } else if (!strncasecmp(buf, "CP", 2)) { |
| pdata->uart_path = MUIC_PATH_UART_CP; |
| switch_to_cp_uart(muic_data, muic_data->attached_dev); |
| } else { |
| pr_info("[%s:%s] invalid value\n", MUIC_DEV_NAME, __func__); |
| } |
| |
| pr_info("[%s:%s] uart_path(%d)\n", MUIC_DEV_NAME, __func__, |
| pdata->uart_path); |
| |
| return count; |
| } |
| |
| static ssize_t sm5713_muic_show_usb_sel(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct sm5713_muic_data *muic_data = dev_get_drvdata(dev); |
| struct muic_platform_data *pdata = muic_data->pdata; |
| const char *mode = "UNKNOWN\n"; |
| |
| switch (pdata->usb_path) { |
| case MUIC_PATH_USB_AP: |
| mode = "PDA\n"; |
| break; |
| case MUIC_PATH_USB_CP: |
| mode = "MODEM\n"; |
| break; |
| default: |
| break; |
| } |
| |
| pr_info("[%s:%s] usb_sel(%s)\n", MUIC_DEV_NAME, __func__, mode); |
| |
| return sprintf(buf, mode); |
| } |
| |
| static ssize_t sm5713_muic_set_usb_sel(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, size_t count) |
| { |
| struct sm5713_muic_data *muic_data = dev_get_drvdata(dev); |
| struct muic_platform_data *pdata = muic_data->pdata; |
| |
| if (!strncasecmp(buf, "PDA", 3)) |
| pdata->usb_path = MUIC_PATH_USB_AP; |
| else if (!strncasecmp(buf, "MODEM", 5)) |
| pdata->usb_path = MUIC_PATH_USB_CP; |
| else |
| pr_info("[%s:%s] invalid value\n", MUIC_DEV_NAME, __func__); |
| |
| pr_info("[%s:%s] usb_path(%d)\n", MUIC_DEV_NAME, __func__, |
| pdata->usb_path); |
| |
| return count; |
| } |
| #endif |
| |
| static ssize_t sm5713_muic_show_adc(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct sm5713_muic_data *muic_data = dev_get_drvdata(dev); |
| int ret = 0; |
| |
| mutex_lock(&muic_data->muic_mutex); |
| |
| if (muic_data->is_factory_start) |
| #if defined(CONFIG_IF_CB_MANAGER) |
| ret = usbpd_sbu_test_read(muic_data->man); |
| #else |
| ret = 0; |
| #endif |
| else |
| ret = sm5713_muic_get_adc(muic_data); |
| |
| pr_info("[%s:%s] attached_dev: %d, adc = %d, is_factory_start = %d\n", |
| MUIC_DEV_NAME, __func__, muic_data->attached_dev, |
| ret, muic_data->is_factory_start); |
| |
| mutex_unlock(&muic_data->muic_mutex); |
| if (ret < 0) { |
| pr_err("[%s:%s] err read adc reg(%d)\n", |
| MUIC_DEV_NAME, __func__, ret); |
| return sprintf(buf, "UNKNOWN\n"); |
| } |
| |
| return sprintf(buf, "%x\n", (ret & 0x1F)); |
| } |
| |
| static ssize_t sm5713_muic_show_usb_state(struct device *dev, |
| struct device_attribute *attr, |
| char *buf) |
| { |
| struct sm5713_muic_data *muic_data = dev_get_drvdata(dev); |
| |
| switch (muic_data->attached_dev) { |
| case ATTACHED_DEV_USB_MUIC: |
| case ATTACHED_DEV_CDP_MUIC: |
| case ATTACHED_DEV_JIG_USB_OFF_MUIC: |
| case ATTACHED_DEV_JIG_USB_ON_MUIC: |
| return sprintf(buf, "USB_STATE_CONFIGURED\n"); |
| default: |
| break; |
| } |
| |
| return 0; |
| } |
| |
| #ifdef DEBUG_MUIC |
| static ssize_t sm5713_muic_show_mansw(struct device *dev, |
| struct device_attribute *attr, |
| char *buf) |
| { |
| struct sm5713_muic_data *muic_data = dev_get_drvdata(dev); |
| int ret = 0; |
| |
| mutex_lock(&muic_data->muic_mutex); |
| ret = sm5713_i2c_read_byte(muic_data->i2c, SM5713_MUIC_REG_MANUAL_SW); |
| mutex_unlock(&muic_data->muic_mutex); |
| |
| pr_info("[%s:%s] manual sw:%d buf%s\n", MUIC_DEV_NAME, __func__, |
| ret, buf); |
| |
| if (ret < 0) { |
| pr_err("[%s:%s] fail to read muic reg\n", MUIC_DEV_NAME, |
| __func__); |
| return sprintf(buf, "UNKNOWN\n"); |
| } |
| return sprintf(buf, "0x%x\n", ret); |
| } |
| |
| static ssize_t sm5713_muic_show_registers(struct device *dev, |
| struct device_attribute *attr, |
| char *buf) |
| { |
| struct sm5713_muic_data *muic_data = dev_get_drvdata(dev); |
| char mesg[256] = ""; |
| |
| mutex_lock(&muic_data->muic_mutex); |
| sm5713_read_reg_dump(muic_data, mesg); |
| mutex_unlock(&muic_data->muic_mutex); |
| pr_info("[%s:%s] %s\n", MUIC_DEV_NAME, __func__, mesg); |
| |
| return sprintf(buf, "%s\n", mesg); |
| } |
| #endif |
| |
| #if defined(CONFIG_USB_HOST_NOTIFY) |
| static ssize_t sm5713_muic_show_otg_test(struct device *dev, |
| struct device_attribute *attr, |
| char *buf) |
| { |
| pr_info("[%s:%s] buf%s\n", MUIC_DEV_NAME, __func__, buf); |
| |
| return sprintf(buf, "\n"); |
| } |
| |
| static ssize_t sm5713_muic_set_otg_test(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, size_t count) |
| { |
| struct sm5713_muic_data *muic_data = dev_get_drvdata(dev); |
| |
| pr_info("[%s:%s] buf:%s\n", MUIC_DEV_NAME, __func__, buf); |
| |
| /* The otg_test is set 0 durring the otg test. Not 1!!! */ |
| if (!strncmp(buf, "0", 1)) { |
| muic_data->is_otg_test = 1; |
| } else if (!strncmp(buf, "1", 1)) { |
| muic_data->is_otg_test = 0; |
| } else { |
| pr_info("[%s:%s] Wrong command\n", MUIC_DEV_NAME, __func__); |
| return count; |
| } |
| |
| return count; |
| } |
| #endif |
| |
| static ssize_t sm5713_muic_show_attached_dev(struct device *dev, |
| struct device_attribute *attr, |
| char *buf) |
| { |
| struct sm5713_muic_data *muic_data = dev_get_drvdata(dev); |
| int mdev = muic_data->attached_dev; |
| |
| pr_info("[%s:%s] attached_dev:%d\n", MUIC_DEV_NAME, __func__, mdev); |
| |
| switch (mdev) { |
| case ATTACHED_DEV_NONE_MUIC: |
| return sprintf(buf, "No VPS\n"); |
| case ATTACHED_DEV_USB_MUIC: |
| return sprintf(buf, "USB\n"); |
| case ATTACHED_DEV_CDP_MUIC: |
| return sprintf(buf, "CDP\n"); |
| case ATTACHED_DEV_OTG_MUIC: |
| return sprintf(buf, "OTG\n"); |
| case ATTACHED_DEV_TA_MUIC: |
| return sprintf(buf, "TA\n"); |
| case ATTACHED_DEV_JIG_UART_OFF_MUIC: |
| return sprintf(buf, "JIG UART OFF\n"); |
| case ATTACHED_DEV_JIG_UART_OFF_VB_MUIC: |
| return sprintf(buf, "JIG UART OFF/VB\n"); |
| case ATTACHED_DEV_JIG_UART_ON_MUIC: |
| return sprintf(buf, "JIG UART ON\n"); |
| case ATTACHED_DEV_JIG_UART_ON_VB_MUIC: |
| return sprintf(buf, "JIG UART ON/VB\n"); |
| case ATTACHED_DEV_JIG_USB_OFF_MUIC: |
| return sprintf(buf, "JIG USB OFF\n"); |
| case ATTACHED_DEV_JIG_USB_ON_MUIC: |
| return sprintf(buf, "JIG USB ON\n"); |
| case ATTACHED_DEV_DESKDOCK_MUIC: |
| return sprintf(buf, "DESKDOCK\n"); |
| case ATTACHED_DEV_AUDIODOCK_MUIC: |
| return sprintf(buf, "AUDIODOCK\n"); |
| case ATTACHED_DEV_CHARGING_CABLE_MUIC: |
| return sprintf(buf, "PS CABLE\n"); |
| case ATTACHED_DEV_AFC_CHARGER_DISABLED_MUIC: |
| case ATTACHED_DEV_AFC_CHARGER_5V_MUIC: |
| case ATTACHED_DEV_AFC_CHARGER_9V_MUIC: |
| case ATTACHED_DEV_QC_CHARGER_5V_MUIC: |
| case ATTACHED_DEV_QC_CHARGER_9V_MUIC: |
| return sprintf(buf, "AFC Charger\n"); |
| case ATTACHED_DEV_TIMEOUT_OPEN_MUIC: |
| return sprintf(buf, "DCD Timeout\n"); |
| case ATTACHED_DEV_POGO_DOCK_MUIC: |
| return sprintf(buf, "POGO Dock\n"); |
| case ATTACHED_DEV_POGO_DOCK_5V_MUIC: |
| return sprintf(buf, "POGO Dock/5V\n"); |
| case ATTACHED_DEV_POGO_DOCK_9V_MUIC: |
| return sprintf(buf, "POGO Dock/9V\n"); |
| default: |
| break; |
| } |
| |
| return sprintf(buf, "UNKNOWN\n"); |
| } |
| |
| static ssize_t sm5713_muic_show_audio_path(struct device *dev, |
| struct device_attribute *attr, |
| char *buf) |
| { |
| return 0; |
| } |
| |
| static ssize_t sm5713_muic_set_audio_path(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, size_t count) |
| { |
| return 0; |
| } |
| |
| static ssize_t sm5713_muic_show_apo_factory(struct device *dev, |
| struct device_attribute *attr, |
| char *buf) |
| { |
| struct sm5713_muic_data *muic_data = dev_get_drvdata(dev); |
| const char *mode; |
| |
| /* true: Factory mode, false: not Factory mode */ |
| if (muic_data->is_factory_start) |
| mode = "FACTORY_MODE"; |
| else |
| mode = "NOT_FACTORY_MODE"; |
| |
| pr_info("[%s:%s] %s\n", |
| MUIC_DEV_NAME, __func__, mode); |
| |
| return sprintf(buf, "%s\n", mode); |
| } |
| |
| static ssize_t sm5713_muic_set_apo_factory(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, size_t count) |
| { |
| struct sm5713_muic_data *muic_data = dev_get_drvdata(dev); |
| const char *mode; |
| |
| pr_info("[%s:%s] buf:%s\n", |
| MUIC_DEV_NAME, __func__, buf); |
| |
| /* "FACTORY_START": factory mode */ |
| if (!strncmp(buf, "FACTORY_START", 13)) { |
| muic_data->is_factory_start = true; |
| mode = "FACTORY_MODE"; |
| } else { |
| pr_info("[%s:%s] Wrong command\n", MUIC_DEV_NAME, __func__); |
| return count; |
| } |
| |
| return count; |
| } |
| |
| static int sm5713_get_vbus_value(struct sm5713_muic_data *muic_data) |
| { |
| struct i2c_client *i2c = muic_data->i2c; |
| int vbus_voltage = 0, voltage1 = 0, voltage2 = 0; |
| int vol = 0; |
| int irqvbus = 0; |
| int intmask2 = 0; |
| int retry = 0; |
| |
| intmask2 = sm5713_i2c_read_byte(i2c, SM5713_MUIC_REG_INTMASK2); |
| intmask2 = intmask2 | 0x04; |
| sm5713_i2c_write_byte(i2c, SM5713_MUIC_REG_INTMASK2, intmask2); |
| |
| sm5713_set_afc_ctrl_reg(muic_data, AFCCTRL_VBUS_READ, 1); |
| |
| for (retry = 0; retry < 10 ; retry++) { |
| msleep(20); |
| irqvbus = sm5713_i2c_read_byte(i2c, SM5713_MUIC_REG_INT2); |
| if (irqvbus & INT2_VBUS_UPDATE_MASK) { |
| pr_info("[%s:%s] VBUS update Success(%d), retry: %d)\n", |
| MUIC_DEV_NAME, __func__, irqvbus, retry); |
| break; |
| } |
| pr_info("[%s:%s] VBUS update Fail(%d), retry: %d)\n", |
| MUIC_DEV_NAME, __func__, irqvbus, retry); |
| } |
| intmask2 = intmask2 & 0xFB; |
| sm5713_i2c_write_byte(i2c, SM5713_MUIC_REG_INTMASK2, intmask2); |
| |
| sm5713_set_afc_ctrl_reg(muic_data, AFCCTRL_VBUS_READ, 0); |
| |
| if (retry >= 10) { |
| pr_info("[%s:%s] VBUS update Failed(%d)\n", MUIC_DEV_NAME, |
| __func__, retry); |
| return 0; |
| } |
| |
| voltage1 = sm5713_i2c_read_byte(i2c, SM5713_MUIC_REG_VBUS_VOLTAGE1); |
| voltage2 = sm5713_i2c_read_byte(i2c, SM5713_MUIC_REG_VBUS_VOLTAGE2); |
| if (voltage1 < 0) |
| pr_err("[%s:%s] err read VBUS VOLTAGE1(0x%2x)\n", MUIC_DEV_NAME, |
| __func__, voltage1); |
| |
| vbus_voltage = voltage1*1000 + (voltage2*3900)/1000; |
| |
| pr_info("[%s:%s] voltage1=[0x%02x] voltage2=[0x%02x] vbus_voltage=%d mV, attached_dev(%d)\n", |
| MUIC_DEV_NAME, __func__, voltage1, voltage2, vbus_voltage, |
| muic_data->attached_dev); |
| |
| if ((vbus_voltage > 4500) && (vbus_voltage <= 5500)) |
| vol = 5; |
| else if ((vbus_voltage > 5500) && (vbus_voltage <= 6500)) |
| vol = 6; |
| else if ((vbus_voltage > 6500) && (vbus_voltage <= 7500)) |
| vol = 7; |
| #if defined(CONFIG_SEC_FACTORY) |
| else if ((vbus_voltage > 7500) && (vbus_voltage <= 8000)) |
| vol = 8; |
| else if ((vbus_voltage > 8000) && (vbus_voltage <= 9500)) |
| vol = 9; |
| #else |
| else if ((vbus_voltage > 7500) && (vbus_voltage <= 8500)) |
| vol = 8; |
| else if ((vbus_voltage > 8500) && (vbus_voltage <= 9500)) |
| vol = 9; |
| #endif |
| else if ((vbus_voltage > 9500) && (vbus_voltage <= 10500)) |
| vol = 10; |
| else |
| vol = voltage1; |
| |
| pr_info("[%s:%s] VBUS:%d\n", MUIC_DEV_NAME, __func__, vol); |
| |
| return vol; |
| } |
| |
| static ssize_t muic_show_vbus_value(struct device *dev, |
| struct device_attribute *attr, |
| char *buf) |
| { |
| struct sm5713_muic_data *muic_data = dev_get_drvdata(dev); |
| int vol = sm5713_get_vbus_value(muic_data); |
| |
| return sprintf(buf, "%dV\n", vol); |
| } |
| |
| static ssize_t sm5713_muic_show_afc_disable(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct sm5713_muic_data *muic_data = dev_get_drvdata(dev); |
| struct muic_platform_data *pdata = muic_data->pdata; |
| |
| if (pdata->afc_disable) { |
| pr_info("[%s:%s] AFC DISABLE\n", MUIC_DEV_NAME, __func__); |
| return sprintf(buf, "1\n"); |
| } |
| |
| pr_info("[%s:%s] AFC ENABLE", MUIC_DEV_NAME, __func__); |
| return sprintf(buf, "0\n"); |
| } |
| |
| static ssize_t sm5713_muic_set_afc_disable(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, size_t count) |
| { |
| struct sm5713_muic_data *muic_data = dev_get_drvdata(dev); |
| struct muic_platform_data *pdata = muic_data->pdata; |
| #ifdef CM_OFFSET |
| int ret; |
| char param_val; |
| union power_supply_propval psy_val; |
| #endif |
| |
| if (!strncasecmp(buf, "1", 1)) |
| pdata->afc_disable = true; |
| else if (!strncasecmp(buf, "0", 1)) |
| pdata->afc_disable = false; |
| else |
| pr_warn("[%s:%s] invalid value\n", MUIC_DEV_NAME, __func__); |
| |
| #ifdef CM_OFFSET |
| param_val = (pdata->afc_disable) ? '1' : '0'; |
| |
| ret = sec_set_param(CM_OFFSET + 1, param_val); |
| if (ret < 0) |
| pr_err("[%s:%s] set_param failed(%d)\n", MUIC_DEV_NAME, |
| __func__, ret); |
| |
| psy_val.intval = param_val; |
| psy_do_property("battery", set, POWER_SUPPLY_EXT_PROP_HV_DISABLE, psy_val); |
| #else |
| pr_err("%s:set_param is NOT supported!\n", __func__); |
| #endif |
| |
| pr_info("[%s:%s] attached_dev(%d), afc_disable(%d)\n", |
| MUIC_DEV_NAME, __func__, muic_data->attached_dev, pdata->afc_disable); |
| |
| if (pdata->afc_disable) { |
| if (muic_data->attached_dev == ATTACHED_DEV_AFC_CHARGER_9V_MUIC || |
| muic_data->attached_dev == ATTACHED_DEV_AFC_CHARGER_PREPARE_MUIC || |
| muic_data->attached_dev == ATTACHED_DEV_AFC_CHARGER_5V_MUIC || |
| muic_data->attached_dev == ATTACHED_DEV_QC_CHARGER_9V_MUIC || |
| muic_data->attached_dev == ATTACHED_DEV_QC_CHARGER_PREPARE_MUIC || |
| muic_data->attached_dev == ATTACHED_DEV_QC_CHARGER_5V_MUIC || |
| muic_data->attached_dev == ATTACHED_DEV_AFC_CHARGER_DISABLED_MUIC) { |
| sm5713_set_afc_ctrl_reg(muic_data, AFCCTRL_DP_RESET, 1); /*DP_RESET*/ |
| msleep(50); |
| |
| muic_data->attached_dev = ATTACHED_DEV_AFC_CHARGER_DISABLED_MUIC; |
| muic_notifier_attach_attached_dev(muic_data->attached_dev); |
| } |
| } else { |
| if (muic_data->attached_dev == ATTACHED_DEV_TA_MUIC || |
| muic_data->attached_dev == ATTACHED_DEV_AFC_CHARGER_DISABLED_MUIC) |
| sm5713_set_afc_ctrl_reg(muic_data, AFCCTRL_DP_RESET, 1); /*DP_RESET*/ |
| } |
| |
| return count; |
| } |
| |
| #if defined(CONFIG_HICCUP_CHARGER) |
| static ssize_t hiccup_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| return sprintf(buf, "ENABLE\n"); |
| } |
| |
| static ssize_t hiccup_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t count) |
| { |
| struct sm5713_muic_data *muic_data = dev_get_drvdata(dev); |
| |
| if (!strncasecmp(buf, "DISABLE", 7)) { |
| muic_data->is_hiccup_mode = false; |
| com_to_open(muic_data); |
| } else |
| pr_warn("%s invalid com : %s\n", __func__, buf); |
| |
| return count; |
| } |
| #endif |
| |
| static DEVICE_ATTR(uart_en, 0664, sm5713_muic_show_uart_en, |
| sm5713_muic_set_uart_en); |
| #ifndef CONFIG_UART_SWITCH |
| static DEVICE_ATTR(uart_sel, 0664, sm5713_muic_show_uart_sel, |
| sm5713_muic_set_uart_sel); |
| #endif |
| static DEVICE_ATTR(adc, 0444, sm5713_muic_show_adc, NULL); |
| #ifdef DEBUG_MUIC |
| static DEVICE_ATTR(mansw, 0444, sm5713_muic_show_mansw, NULL); |
| static DEVICE_ATTR(dump_registers, 0444, sm5713_muic_show_registers, NULL); |
| #endif |
| static DEVICE_ATTR(usb_state, 0444, sm5713_muic_show_usb_state, NULL); |
| #if defined(CONFIG_USB_HOST_NOTIFY) |
| static DEVICE_ATTR(otg_test, 0664, |
| sm5713_muic_show_otg_test, sm5713_muic_set_otg_test); |
| #endif |
| static DEVICE_ATTR(attached_dev, 0444, sm5713_muic_show_attached_dev, NULL); |
| static DEVICE_ATTR(audio_path, 0664, |
| sm5713_muic_show_audio_path, sm5713_muic_set_audio_path); |
| static DEVICE_ATTR(apo_factory, 0664, |
| sm5713_muic_show_apo_factory, |
| sm5713_muic_set_apo_factory); |
| #ifndef CONFIG_UART_SWITCH |
| static DEVICE_ATTR(usb_sel, 0664, |
| sm5713_muic_show_usb_sel, |
| sm5713_muic_set_usb_sel); |
| #endif |
| static DEVICE_ATTR(vbus_value, 0444, muic_show_vbus_value, NULL); |
| |
| static DEVICE_ATTR(afc_disable, 0664, |
| sm5713_muic_show_afc_disable, sm5713_muic_set_afc_disable); |
| #if defined(CONFIG_HICCUP_CHARGER) |
| static DEVICE_ATTR_RW(hiccup); |
| #endif |
| |
| |
| static struct attribute *sm5713_muic_attributes[] = { |
| &dev_attr_uart_en.attr, |
| #ifndef CONFIG_UART_SWITCH |
| &dev_attr_uart_sel.attr, |
| #endif |
| &dev_attr_adc.attr, |
| #ifdef DEBUG_MUIC |
| &dev_attr_mansw.attr, |
| &dev_attr_dump_registers.attr, |
| #endif |
| &dev_attr_usb_state.attr, |
| #if defined(CONFIG_USB_HOST_NOTIFY) |
| &dev_attr_otg_test.attr, |
| #endif |
| &dev_attr_attached_dev.attr, |
| &dev_attr_audio_path.attr, |
| &dev_attr_apo_factory.attr, |
| #ifndef CONFIG_UART_SWITCH |
| &dev_attr_usb_sel.attr, |
| #endif |
| &dev_attr_vbus_value.attr, |
| &dev_attr_afc_disable.attr, |
| #if defined(CONFIG_HICCUP_CHARGER) |
| &dev_attr_hiccup.attr, |
| #endif |
| NULL |
| }; |
| |
| static const struct attribute_group sm5713_muic_group = { |
| .attrs = sm5713_muic_attributes, |
| }; |
| |
| #if 0 |
| static int set_ctrl_reg(struct sm5713_muic_data *muic_data, int shift, bool on) |
| { |
| struct i2c_client *i2c = muic_data->i2c; |
| u8 reg_val; |
| int ret = 0; |
| |
| ret = sm5713_i2c_read_byte(i2c, SM5713_MUIC_REG_CNTL); |
| if (ret < 0) |
| pr_err("[%s:%s] err read CTRL(%d)\n", |
| MUIC_DEV_NAME, __func__, ret); |
| |
| if (on) |
| reg_val = ret | (0x1 << shift); |
| else |
| reg_val = ret & ~(0x1 << shift); |
| |
| if (reg_val ^ ret) { |
| pr_info("[%s:%s] 0x%x != 0x%x, update\n", |
| MUIC_DEV_NAME, __func__, reg_val, ret); |
| |
| ret = sm5713_i2c_guaranteed_wbyte(i2c, SM5713_MUIC_REG_CNTL, |
| reg_val); |
| if (ret < 0) |
| pr_err("[%s:%s] err write(%d)\n", |
| MUIC_DEV_NAME, __func__, ret); |
| } else { |
| pr_info("[%s:%s] 0x%x == 0x%x, just return\n", |
| MUIC_DEV_NAME, __func__, reg_val, ret); |
| return 0; |
| } |
| |
| ret = sm5713_i2c_read_byte(i2c, SM5713_MUIC_REG_CNTL); |
| if (ret < 0) |
| pr_err("[%s:%s] err read CTRL(%d)\n", MUIC_DEV_NAME, |
| __func__, ret); |
| else |
| pr_info("[%s:%s] after change(0x%x)\n", |
| MUIC_DEV_NAME, __func__, ret); |
| |
| return ret; |
| } |
| |
| static int set_bc12off(struct sm5713_muic_data *muic_data, bool on) |
| { |
| int shift = CTRL_BC12OFF_MASK; |
| int ret = 0; |
| |
| ret = set_ctrl_reg(muic_data, shift, on); |
| |
| return ret; |
| } |
| #endif |
| |
| static int set_manual_sw(struct sm5713_muic_data *muic_data, bool on) |
| { |
| struct i2c_client *i2c = muic_data->i2c; |
| u8 reg_val = 0; |
| int shift = CTRL_MANUAL_SW_SHIFT; |
| int ret = 0; |
| |
| ret = sm5713_i2c_read_byte(i2c, SM5713_MUIC_REG_CFG1); |
| if (ret < 0) |
| pr_err("[%s:%s] err read CTRL(%d)\n", |
| MUIC_DEV_NAME, __func__, ret); |
| |
| pr_info("[%s:%s] on:0x%x, cfg1=0x%x\n", MUIC_DEV_NAME, __func__, |
| on, ret); |
| |
| if (on) |
| reg_val = ret | (0x1 << shift); |
| else |
| reg_val = ret & ~(0x1 << shift); |
| |
| if (reg_val ^ ret) { |
| pr_info("[%s:%s] 0x%x != 0x%x, update\n", |
| MUIC_DEV_NAME, __func__, reg_val, ret); |
| |
| ret = sm5713_i2c_guaranteed_wbyte(i2c, SM5713_MUIC_REG_CFG1, |
| reg_val); |
| if (ret < 0) |
| pr_err("[%s:%s] err write(%d)\n", |
| MUIC_DEV_NAME, __func__, ret); |
| } else { |
| pr_info("[%s:%s] 0x%x == 0x%x, just return\n", |
| MUIC_DEV_NAME, __func__, reg_val, ret); |
| return 0; |
| } |
| |
| ret = sm5713_i2c_read_byte(i2c, SM5713_MUIC_REG_CFG1); |
| if (ret < 0) |
| pr_err("[%s:%s] err read CTRL(%d)\n", |
| MUIC_DEV_NAME, __func__, ret); |
| else |
| pr_info("[%s:%s] after change(0x%x)\n", |
| MUIC_DEV_NAME, __func__, ret); |
| |
| return ret; |
| } |
| |
| static int get_com_sw(struct sm5713_muic_data *muic_data) |
| { |
| struct i2c_client *i2c = muic_data->i2c; |
| int temp = 0; |
| |
| temp = sm5713_i2c_read_byte(i2c, SM5713_MUIC_REG_MANUAL_SW); |
| |
| pr_info("[%s:%s] 0x%x\n", MUIC_DEV_NAME, __func__, temp); |
| |
| return temp; |
| } |
| |
| static int set_com_sw(struct sm5713_muic_data *muic_data, |
| enum sm5713_reg_manual_sw_value reg_val) |
| { |
| struct i2c_client *i2c = muic_data->i2c; |
| int ret = 0; |
| int temp = 0; |
| |
| /* --- MANSW [5:3][2:0] : DM DP --- */ |
| temp = sm5713_i2c_read_byte(i2c, SM5713_MUIC_REG_MANUAL_SW); |
| if (temp < 0) |
| pr_err("[%s:%s] err read MANSW(0x%x)\n", |
| MUIC_DEV_NAME, __func__, temp); |
| |
| pr_info("[%s:%s]0x%x != 0x%x, update\n", MUIC_DEV_NAME, __func__, |
| reg_val, temp); |
| |
| ret = sm5713_i2c_guaranteed_wbyte(i2c, |
| SM5713_MUIC_REG_MANUAL_SW, reg_val); |
| if (ret < 0) |
| pr_err("[%s:%s] err write MANSW(0x%x)\n", |
| MUIC_DEV_NAME, __func__, reg_val); |
| |
| return ret; |
| } |
| |
| static int com_to_open(struct sm5713_muic_data *muic_data) |
| { |
| enum sm5713_reg_manual_sw_value reg_val; |
| int ret = 0; |
| |
| pr_info("[%s:%s]\n", MUIC_DEV_NAME, __func__); |
| reg_val = MANSW_OPEN; |
| ret = set_com_sw(muic_data, reg_val); |
| if (ret) |
| pr_err("[%s:%s] set_com_sw err\n", MUIC_DEV_NAME, __func__); |
| |
| set_manual_sw(muic_data, true); /* false(0):auto true(1):manual */ |
| |
| return ret; |
| } |
| |
| static int com_to_usb(struct sm5713_muic_data *muic_data) |
| { |
| enum sm5713_reg_manual_sw_value reg_val; |
| int ret = 0; |
| |
| pr_info("[%s:%s]\n", MUIC_DEV_NAME, __func__); |
| reg_val = MANSW_USB; |
| ret = set_com_sw(muic_data, reg_val); |
| if (ret) |
| pr_err("[%s:%s]set_com_usb err\n", MUIC_DEV_NAME, __func__); |
| |
| set_manual_sw(muic_data, true); /* false(0):auto true(1):manual */ |
| |
| return ret; |
| } |
| |
| static int com_to_uart(struct sm5713_muic_data *muic_data) |
| { |
| enum sm5713_reg_manual_sw_value reg_val; |
| int ret = 0; |
| |
| if (muic_data->is_rustproof) { |
| pr_info("[%s:%s] rustproof mode\n", MUIC_DEV_NAME, __func__); |
| return ret; |
| } |
| reg_val = MANSW_UART; |
| ret = set_com_sw(muic_data, reg_val); |
| if (ret) |
| pr_err("[%s:%s] set_com_uart err\n", MUIC_DEV_NAME, __func__); |
| |
| set_manual_sw(muic_data, true); /* false(0):auto true(1):manual */ |
| |
| return ret; |
| } |
| |
| #if defined(CONFIG_HICCUP_CHARGER) |
| static int com_to_audio(struct sm5713_muic_data *muic_data) |
| { |
| enum sm5713_reg_manual_sw_value reg_val; |
| int ret = 0; |
| |
| reg_val = MANSW_AUDIO; |
| ret = set_com_sw(muic_data, reg_val); |
| if (ret) |
| pr_err("[%s:%s] set_com_audio err\n", MUIC_DEV_NAME, __func__); |
| |
| set_manual_sw(muic_data, true); /* false(0):auto true(1):manual */ |
| |
| return ret; |
| } |
| |
| static int switch_to_ap_audio(struct sm5713_muic_data *muic_data) |
| { |
| int ret = 0; |
| |
| pr_info("[%s:%s]\n", MUIC_DEV_NAME, __func__); |
| |
| ret = com_to_audio(muic_data); |
| if (ret) { |
| pr_err("[%s:%s] com->audio set err\n", MUIC_DEV_NAME, __func__); |
| return ret; |
| } |
| |
| return ret; |
| } |
| #endif |
| |
| static int switch_to_ap_uart(struct sm5713_muic_data *muic_data, int new_dev) |
| { |
| int ret = 0; |
| |
| pr_info("[%s:%s]\n", MUIC_DEV_NAME, __func__); |
| |
| switch (new_dev) { |
| case ATTACHED_DEV_JIG_UART_OFF_MUIC: |
| case ATTACHED_DEV_JIG_UART_OFF_VB_MUIC: |
| case ATTACHED_DEV_JIG_UART_ON_MUIC: |
| case ATTACHED_DEV_JIG_UART_ON_VB_MUIC: |
| ret = com_to_uart(muic_data); |
| #if defined(CONFIG_CP_UART_NOTI) |
| send_uart_noti_to_modem(MODEM_CTRL_UART_AP); |
| #endif |
| break; |
| default: |
| ret = -EINVAL; |
| break; |
| } |
| |
| if (ret) { |
| pr_err("[%s:%s] com->uart set err\n", MUIC_DEV_NAME, __func__); |
| return ret; |
| } |
| |
| return ret; |
| } |
| |
| static int switch_to_cp_uart(struct sm5713_muic_data *muic_data, int new_dev) |
| { |
| int ret = 0; |
| |
| pr_info("[%s:%s]\n", MUIC_DEV_NAME, __func__); |
| |
| switch (new_dev) { |
| case ATTACHED_DEV_JIG_UART_OFF_MUIC: |
| case ATTACHED_DEV_JIG_UART_OFF_VB_MUIC: |
| case ATTACHED_DEV_JIG_UART_ON_MUIC: |
| case ATTACHED_DEV_JIG_UART_ON_VB_MUIC: |
| ret = com_to_uart(muic_data); |
| #if defined(CONFIG_CP_UART_NOTI) |
| send_uart_noti_to_modem(MODEM_CTRL_UART_CP); |
| #endif |
| break; |
| default: |
| ret = -EINVAL; |
| break; |
| } |
| |
| if (ret) { |
| pr_err("[%s:%s] com->uart set err\n", MUIC_DEV_NAME, __func__); |
| return ret; |
| } |
| |
| return ret; |
| } |
| |
| static int attach_jig_uart_path(struct sm5713_muic_data *muic_data, int new_dev) |
| { |
| struct muic_platform_data *pdata = muic_data->pdata; |
| int ret = 0; |
| |
| if (pdata->uart_path == MUIC_PATH_UART_AP) |
| ret = switch_to_ap_uart(muic_data, new_dev); |
| else |
| ret = switch_to_cp_uart(muic_data, new_dev); |
| |
| return ret; |
| } |
| |
| #if defined(CONFIG_MUIC_BCD_RESCAN) |
| static void sm5713_muic_BCD_rescan(struct sm5713_muic_data *muic_data) |
| { |
| struct i2c_client *i2c = muic_data->i2c; |
| int reg_ctrl1 = 0, reg_ctrl2 = 0; |
| |
| com_to_open(muic_data); |
| |
| pr_info("[%s:%s] BCD_RESCAN\n", MUIC_DEV_NAME, __func__); |
| |
| reg_ctrl1 = sm5713_i2c_read_byte(i2c, SM5713_MUIC_REG_REVID2); |
| reg_ctrl2 = reg_ctrl1 | 0x10; |
| sm5713_i2c_write_byte(i2c, SM5713_MUIC_REG_REVID2, reg_ctrl2); |
| |
| reg_ctrl2 = reg_ctrl2 & 0xEF; |
| sm5713_i2c_write_byte(i2c, SM5713_MUIC_REG_REVID2, reg_ctrl2); |
| |
| pr_info("[%s:%s] reg_ctrl1=0x%x, reg_ctrl2=0x%x\n", MUIC_DEV_NAME, |
| __func__, reg_ctrl1, reg_ctrl2); |
| } |
| |
| static int sm5713_muic_bc12_retry_check(struct sm5713_muic_data *muic_data) |
| { |
| struct i2c_client *i2c = muic_data->i2c; |
| int new_dev = ATTACHED_DEV_TIMEOUT_OPEN_MUIC; |
| int int_bcdrescan = 0, chg_type = 0; |
| int vbvolt = 0; |
| |
| int_bcdrescan = sm5713_i2c_read_byte(i2c, SM5713_MUIC_REG_INT_BCD_RESCAN); |
| chg_type = sm5713_i2c_read_byte(i2c, SM5713_MUIC_REG_CHG_TYPE); |
| vbvolt = sm5713_i2c_read_byte(i2c, 0x3E); |
| vbvolt = vbvolt & 0x01; |
| pr_info("[%s:%s]: INT_BCD_RESCAN=[0x%02x], CHG_TYPE=[0x%02x], vbvolt=%d\n", |
| MUIC_DEV_NAME, __func__, int_bcdrescan, chg_type, vbvolt); |
| |
| if (int_bcdrescan & 0x40) { /* bit 6 : Interrupts Occurred After BCD rescan Operation*/ |
| if (chg_type == 0x01) { /* DCP */ |
| new_dev = ATTACHED_DEV_TA_MUIC; |
| pr_info("[%s:%s] DCP\n", MUIC_DEV_NAME, __func__); |
| } else if (chg_type == 0x02) { /* CDP */ |
| new_dev = ATTACHED_DEV_CDP_MUIC; |
| pr_info("[%s:%s] CDP\n", MUIC_DEV_NAME, __func__); |
| } else if (chg_type == 0x04) { /* SDP */ |
| new_dev = ATTACHED_DEV_USB_MUIC; |
| pr_info("[%s:%s] USB(SDP)\n", MUIC_DEV_NAME, __func__); |
| } else if (chg_type == 0x08) { /* DCD_OUT_SDP */ |
| new_dev = ATTACHED_DEV_TIMEOUT_OPEN_MUIC; |
| pr_info("[%s:%s] DCD_OUT_SDP\n", MUIC_DEV_NAME, __func__); |
| } else if (chg_type == 0x10) { /* U200 */ |
| new_dev = ATTACHED_DEV_UNOFFICIAL_TA_MUIC; |
| pr_info("[%s:%s] U200\n", MUIC_DEV_NAME, __func__); |
| } else if (chg_type == 0x11) { /* AFC TA */ |
| new_dev = ATTACHED_DEV_TA_MUIC; |
| pr_info("[%s:%s] DCP(AFC)\n", MUIC_DEV_NAME, __func__); |
| } else if (chg_type == 0x12) { /* LO_TA */ |
| new_dev = ATTACHED_DEV_UNOFFICIAL_TA_MUIC; |
| pr_info("[%s:%s] LO_TA\n", MUIC_DEV_NAME, __func__); |
| } else if (chg_type == 0x13) { /* QC20_TA */ |
| new_dev = ATTACHED_DEV_TA_MUIC; |
| pr_info("[%s:%s] DCP(QC)\n", MUIC_DEV_NAME, __func__); |
| } else { |
| pr_info("[%s:%s] UNKNOWN\n", MUIC_DEV_NAME, __func__); |
| } |
| } else { |
| pr_info("[%s:%s] DCD_OUT_SDP\n", MUIC_DEV_NAME, __func__); |
| } |
| |
| return new_dev; |
| } |
| |
| static void muic_bc12_retry_work(struct work_struct *work) |
| { |
| struct sm5713_muic_data *muic_data = container_of(work, |
| struct sm5713_muic_data, bc12_retry_work.work); |
| |
| pr_info("[%s:%s]\n", MUIC_DEV_NAME, __func__); |
| |
| mutex_lock(&muic_data->muic_mutex); |
| wake_lock(&muic_data->wake_lock); |
| sm5713_muic_detect_dev(muic_data, SM5713_MUIC_IRQ_WORK); |
| wake_unlock(&muic_data->wake_lock); |
| mutex_unlock(&muic_data->muic_mutex); |
| } |
| #endif |
| |
| int sm5713_muic_get_jig_status(void) |
| { |
| struct i2c_client *i2c = static_data->i2c; |
| int dev2 = 0; |
| |
| pr_info("[%s:%s]\n", MUIC_DEV_NAME, __func__); |
| if (static_data == NULL) |
| return 0; |
| |
| dev2 = sm5713_i2c_read_byte(i2c, SM5713_MUIC_REG_DEVICETYPE2); |
| pr_info("[%s:%s]dev2 = 0x%x\n", MUIC_DEV_NAME, __func__, dev2); |
| if ((dev2 & DEV_TYPE2_JIG_UART_OFF) || (dev2 & DEV_TYPE2_JIG_UART_ON)) |
| return 1; |
| |
| return 0; |
| } |
| EXPORT_SYMBOL(sm5713_muic_get_jig_status); |
| |
| #if defined(CONFIG_IF_CB_MANAGER) |
| static int sm5713_muic_if_check_usb_killer(void *data) |
| { |
| struct sm5713_muic_data *muic_data = data; |
| struct i2c_client *i2c; |
| int intmask1 = 0; |
| int usbk_det_en = 0; |
| int intr1 = 0; |
| int dev2 = 0; |
| int ret = 0; |
| |
| pr_info("[%s:%s]\n", MUIC_DEV_NAME, __func__); |
| |
| if (muic_data == NULL) |
| return ret; |
| |
| i2c = muic_data->i2c; |
| |
| /* intmask1 bit6 '1' */ |
| intmask1 = sm5713_i2c_read_byte(i2c, SM5713_MUIC_REG_INTMASK1); |
| intmask1 = intmask1 | 0x40; |
| sm5713_i2c_write_byte(i2c, SM5713_MUIC_REG_INTMASK1, intmask1); |
| |
| /* usbk_det_en bit5 '1' */ |
| usbk_det_en = sm5713_i2c_read_byte(i2c, 0x69); |
| usbk_det_en = usbk_det_en | 0x20; |
| sm5713_i2c_write_byte(i2c, 0x69, usbk_det_en); |
| |
| msleep(10); |
| |
| intr1 = sm5713_i2c_read_byte(i2c, SM5713_MUIC_REG_INT1); |
| if (intr1 & 0x40) { |
| dev2 = sm5713_i2c_read_byte(i2c, SM5713_MUIC_REG_DEVICETYPE2); |
| if (dev2 & 0x40) |
| ret = 1; |
| else |
| ret = 0; |
| } else { |
| ret = 0; |
| } |
| |
| pr_info("[%s:%s] intr1 = 0x%x, dev2 = 0x%x, ret = %d\n", |
| MUIC_DEV_NAME, __func__, intr1, dev2, ret); |
| |
| /* usbk_det_en bit5 '0' */ |
| usbk_det_en = usbk_det_en & 0xDF; |
| sm5713_i2c_write_byte(i2c, 0x69, usbk_det_en); |
| |
| /* intmask1 bit6 '0' */ |
| intmask1 = intmask1 & 0xBF; |
| sm5713_i2c_write_byte(i2c, SM5713_MUIC_REG_INTMASK1, intmask1); |
| |
| return ret; |
| } |
| #endif |
| |
| #if defined(CONFIG_IF_CB_MANAGER) |
| struct muic_ops ops_muic = { |
| .muic_check_usb_killer = sm5713_muic_if_check_usb_killer, |
| }; |
| #endif |
| |
| static int sm5713_muic_get_adc(struct sm5713_muic_data *muic_data) |
| { |
| struct i2c_client *i2c = muic_data->i2c; |
| int adc = 0; |
| |
| adc = sm5713_i2c_read_byte(i2c, SM5713_MUIC_REG_ADC); |
| pr_info("[%s:%s] adc : 0x%X\n", MUIC_DEV_NAME, __func__, adc); |
| |
| return adc; |
| } |
| |
| static int sm5713_muic_get_factory_mode_rid(int new_dev) |
| { |
| int ret = RID_OPEN; |
| |
| switch (new_dev) { |
| case ATTACHED_DEV_JIG_USB_ON_MUIC: |
| ret = RID_301K; |
| break; |
| case ATTACHED_DEV_JIG_UART_OFF_MUIC: |
| case ATTACHED_DEV_JIG_UART_OFF_VB_MUIC: |
| ret = RID_523K; |
| break; |
| case ATTACHED_DEV_JIG_UART_ON_MUIC: |
| case ATTACHED_DEV_JIG_UART_ON_VB_MUIC: |
| ret = RID_619K; |
| break; |
| default: |
| break; |
| } |
| |
| return ret; |
| } |
| |
| static void sm5713_muic_set_factory_mode(struct sm5713_muic_data *muic_data, |
| int new_dev, int intr) |
| { |
| int rid = sm5713_muic_get_factory_mode_rid(new_dev); |
| |
| if (rid == RID_OPEN) |
| return; |
| |
| if (intr != MUIC_INTR_ATTACH) |
| return; |
| |
| #if defined(CONFIG_SEC_FACTORY) |
| if ((muic_data->ccic_info_data.ccic_evt_rid == RID_UNDEFINED) |
| || (muic_data->ccic_info_data.ccic_evt_rid == RID_OPEN)) |
| sm5713_charger_oper_en_factory_mode(DEV_TYPE_SM5713_MUIC, |
| rid, 1); |
| #endif |
| } |
| |
| static void sm5713_muic_handle_logically_detach( |
| struct sm5713_muic_data *muic_data, int new_dev, int irq) |
| { |
| int ret = 0; |
| |
| switch (muic_data->attached_dev) { |
| case ATTACHED_DEV_USB_MUIC: |
| case ATTACHED_DEV_CDP_MUIC: |
| if (new_dev == ATTACHED_DEV_OTG_MUIC) { |
| pr_info("[%s:%s] data role changed, not detach\n", |
| MUIC_DEV_NAME, __func__); |
| return; |
| } |
| ret = com_to_open(muic_data); |
| break; |
| case ATTACHED_DEV_OTG_MUIC: |
| case ATTACHED_DEV_TA_MUIC: |
| case ATTACHED_DEV_JIG_USB_OFF_MUIC: |
| case ATTACHED_DEV_UNOFFICIAL_TA_MUIC: |
| ret = com_to_open(muic_data); |
| break; |
| case ATTACHED_DEV_JIG_USB_ON_MUIC: |
| ret = com_to_open(muic_data); |
| break; |
| case ATTACHED_DEV_JIG_UART_OFF_VB_MUIC: |
| case ATTACHED_DEV_JIG_UART_OFF_MUIC: |
| if (new_dev != ATTACHED_DEV_JIG_UART_OFF_MUIC && |
| new_dev != ATTACHED_DEV_JIG_UART_OFF_VB_MUIC && |
| new_dev != ATTACHED_DEV_JIG_UART_ON_MUIC && |
| new_dev != ATTACHED_DEV_JIG_UART_ON_VB_MUIC) { |
| ret = com_to_open(muic_data); |
| } |
| break; |
| |
| case ATTACHED_DEV_JIG_UART_ON_VB_MUIC: |
| case ATTACHED_DEV_JIG_UART_ON_MUIC: |
| if (new_dev != ATTACHED_DEV_JIG_UART_OFF_MUIC && |
| new_dev != ATTACHED_DEV_JIG_UART_OFF_VB_MUIC && |
| new_dev != ATTACHED_DEV_JIG_UART_ON_MUIC && |
| new_dev != ATTACHED_DEV_JIG_UART_ON_VB_MUIC) { |
| ret = com_to_open(muic_data); |
| } |
| break; |
| #if defined(CONFIG_MUIC_HV_SUPPORT_POGO_DOCK) |
| case ATTACHED_DEV_POGO_DOCK_MUIC: |
| case ATTACHED_DEV_POGO_DOCK_5V_MUIC: |
| case ATTACHED_DEV_POGO_DOCK_9V_MUIC: |
| ret = com_to_open(muic_data); |
| break; |
| #endif /* CONFIG_MUIC_HV_SUPPORT_POGO_DOCK */ |
| default: |
| break; |
| } |
| |
| if (ret) |
| pr_err("[%s:%s] something wrong %d (ERR=%d)\n", |
| MUIC_DEV_NAME, __func__, new_dev, ret); |
| |
| sm5713_muic_set_factory_mode(muic_data, muic_data->attached_dev, |
| MUIC_INTR_DETACH); |
| |
| #if defined(CONFIG_MUIC_NOTIFIER) |
| if (!muic_data->suspended) |
| muic_notifier_detach_attached_dev(muic_data->attached_dev); |
| else |
| muic_data->need_to_noti = true; |
| #endif /* CONFIG_MUIC_NOTIFIER */ |
| } |
| |
| static void sm5713_muic_handle_attach(struct sm5713_muic_data *muic_data, |
| int new_dev, u8 vbvolt, int irq) |
| { |
| int ret = 0; |
| bool noti = (new_dev != muic_data->attached_dev) ? true : false; |
| |
| if (muic_data->is_water_detect) { |
| pr_info("[%s:%s] skipped by water detected condition\n", |
| MUIC_DEV_NAME, __func__); |
| return; |
| } |
| |
| pr_info("[%s:%s] attached_dev:%d, new_dev:%d, suspended:%d\n", |
| MUIC_DEV_NAME, __func__, muic_data->attached_dev, new_dev, |
| muic_data->suspended); |
| |
| if (noti) |
| sm5713_muic_handle_logically_detach(muic_data, new_dev, irq); |
| |
| switch (new_dev) { |
| case ATTACHED_DEV_OTG_MUIC: |
| case ATTACHED_DEV_USB_MUIC: |
| case ATTACHED_DEV_CDP_MUIC: |
| ret = com_to_usb(muic_data); |
| #if defined(CONFIG_MUIC_SUPPORT_CCIC) |
| muic_data->need_to_path_open = true; |
| #endif |
| break; |
| case ATTACHED_DEV_TIMEOUT_OPEN_MUIC: |
| if (get_com_sw(muic_data) == MANSW_UART) |
| pr_info("[%s:%s] skip path setting\n", MUIC_DEV_NAME, |
| __func__); |
| else |
| ret = com_to_usb(muic_data); |
| #if defined(CONFIG_MUIC_SUPPORT_CCIC) |
| muic_data->need_to_path_open = true; |
| #endif |
| break; |
| case ATTACHED_DEV_JIG_USB_OFF_MUIC: |
| case ATTACHED_DEV_JIG_USB_ON_MUIC: |
| ret = com_to_usb(muic_data); |
| break; |
| case ATTACHED_DEV_TA_MUIC: |
| ret = com_to_open(muic_data); |
| break; |
| case ATTACHED_DEV_UNOFFICIAL_TA_MUIC: |
| ret = com_to_open(muic_data); |
| break; |
| case ATTACHED_DEV_JIG_UART_OFF_VB_MUIC: |
| case ATTACHED_DEV_JIG_UART_OFF_MUIC: |
| ret = attach_jig_uart_path(muic_data, new_dev); |
| break; |
| case ATTACHED_DEV_JIG_UART_ON_VB_MUIC: |
| case ATTACHED_DEV_JIG_UART_ON_MUIC: |
| ret = attach_jig_uart_path(muic_data, new_dev); |
| break; |
| case ATTACHED_DEV_UNKNOWN_MUIC: |
| ret = com_to_open(muic_data); |
| break; |
| #if defined(CONFIG_MUIC_HV_SUPPORT_POGO_DOCK) |
| case ATTACHED_DEV_POGO_DOCK_MUIC: |
| case ATTACHED_DEV_POGO_DOCK_5V_MUIC: |
| case ATTACHED_DEV_POGO_DOCK_9V_MUIC: |
| ret = com_to_open(muic_data); |
| break; |
| #endif /* CONFIG_MUIC_HV_SUPPORT_POGO_DOCK */ |
| default: |
| noti = false; |
| pr_info("[%s:%s] unsupported dev=%d, vbus=%c\n", |
| MUIC_DEV_NAME, __func__, new_dev, (vbvolt ? 'O' : 'X')); |
| break; |
| } |
| |
| if (ret) |
| pr_err("[%s:%s] something wrong %d (ERR=%d)\n", |
| MUIC_DEV_NAME, __func__, new_dev, ret); |
| |
| sm5713_muic_set_factory_mode(muic_data, new_dev, MUIC_INTR_ATTACH); |
| |
| pr_info("[%s:%s] done\n", MUIC_DEV_NAME, __func__); |
| |
| muic_data->attached_dev = new_dev; |
| |
| #if defined(CONFIG_MUIC_NOTIFIER) |
| if (noti) { |
| if (!muic_data->suspended) { |
| muic_notifier_attach_attached_dev(new_dev); |
| } else { |
| muic_data->need_to_noti = true; |
| pr_info("[%s:%s] muic_data->need_to_noti = true\n", |
| MUIC_DEV_NAME, __func__); |
| } |
| } else { |
| pr_info("[%s:%s] attach Noti. for (%d) discarded.\n", |
| MUIC_DEV_NAME, __func__, new_dev); |
| } |
| #endif /* CONFIG_MUIC_NOTIFIER */ |
| } |
| |
| static void sm5713_muic_handle_detach(struct sm5713_muic_data *muic_data, |
| int irq) |
| { |
| int ret = 0; |
| bool noti = true; |
| |
| muic_data->hv_voltage = 0; |
| |
| if (muic_data->is_water_detect) { |
| pr_info("[%s:%s] skipped by water detected condition\n", |
| MUIC_DEV_NAME, __func__); |
| return; |
| } |
| |
| pr_info("[%s:%s] attached_dev:%d\n", MUIC_DEV_NAME, __func__, |
| muic_data->attached_dev); |
| |
| switch (muic_data->attached_dev) { |
| case ATTACHED_DEV_JIG_USB_OFF_MUIC: |
| ret = com_to_open(muic_data); |
| break; |
| case ATTACHED_DEV_JIG_USB_ON_MUIC: |
| ret = com_to_open(muic_data); |
| break; |
| case ATTACHED_DEV_USB_MUIC: |
| case ATTACHED_DEV_CDP_MUIC: |
| case ATTACHED_DEV_TIMEOUT_OPEN_MUIC: |
| case ATTACHED_DEV_OTG_MUIC: |
| #if defined(CONFIG_MUIC_SUPPORT_CCIC) |
| if (muic_data->ccic_info_data.ccic_evt_attached == |
| MUIC_CCIC_NOTI_DETACH) { |
| ret = com_to_open(muic_data); |
| muic_data->need_to_path_open = false; |
| } |
| #else |
| ret = com_to_open(muic_data); |
| #endif |
| break; |
| case ATTACHED_DEV_JIG_UART_OFF_VB_MUIC: |
| case ATTACHED_DEV_JIG_UART_OFF_MUIC: |
| ret = com_to_open(muic_data); |
| break; |
| case ATTACHED_DEV_JIG_UART_ON_VB_MUIC: |
| case ATTACHED_DEV_JIG_UART_ON_MUIC: |
| ret = com_to_open(muic_data); |
| break; |
| case ATTACHED_DEV_NONE_MUIC: |
| case ATTACHED_DEV_UNKNOWN_MUIC: |
| #if defined(CONFIG_MUIC_SUPPORT_CCIC) |
| if (muic_data->need_to_path_open) { |
| ret = com_to_open(muic_data); |
| muic_data->need_to_path_open = false; |
| } |
| #endif |
| break; |
| case ATTACHED_DEV_TA_MUIC: |
| case ATTACHED_DEV_AFC_CHARGER_PREPARE_MUIC: |
| case ATTACHED_DEV_AFC_CHARGER_DISABLED_MUIC: |
| case ATTACHED_DEV_AFC_CHARGER_PREPARE_DUPLI_MUIC: |
| case ATTACHED_DEV_AFC_CHARGER_5V_MUIC: |
| case ATTACHED_DEV_AFC_CHARGER_5V_DUPLI_MUIC: |
| case ATTACHED_DEV_AFC_CHARGER_9V_MUIC: |
| case ATTACHED_DEV_QC_CHARGER_5V_MUIC: |
| case ATTACHED_DEV_QC_CHARGER_9V_MUIC: |
| case ATTACHED_DEV_QC_CHARGER_PREPARE_MUIC: |
| case ATTACHED_DEV_AFC_CHARGER_ERR_V_MUIC: |
| case ATTACHED_DEV_AFC_CHARGER_ERR_V_DUPLI_MUIC: |
| case ATTACHED_DEV_QC_CHARGER_ERR_V_MUIC: |
| sm5713_set_afc_ctrl_reg(muic_data, AFCCTRL_ENAFC, 0); |
| ret = com_to_open(muic_data); |
| break; |
| case ATTACHED_DEV_UNOFFICIAL_TA_MUIC: |
| ret = com_to_open(muic_data); |
| break; |
| #if defined(CONFIG_MUIC_HV_SUPPORT_POGO_DOCK) |
| case ATTACHED_DEV_POGO_DOCK_MUIC: |
| case ATTACHED_DEV_POGO_DOCK_5V_MUIC: |
| case ATTACHED_DEV_POGO_DOCK_9V_MUIC: |
| ret = com_to_open(muic_data); |
| break; |
| #endif /* CONFIG_MUIC_HV_SUPPORT_POGO_DOCK */ |
| default: |
| noti = false; |
| pr_info("[%s:%s] invalid type(%d)\n", |
| MUIC_DEV_NAME, __func__, muic_data->attached_dev); |
| break; |
| } |
| if (ret) |
| pr_err("[%s:%s] something wrong %d (ERR=%d)\n", |
| MUIC_DEV_NAME, __func__, muic_data->attached_dev, ret); |
| |
| sm5713_muic_set_factory_mode(muic_data, muic_data->attached_dev, |
| MUIC_INTR_DETACH); |
| |
| #if defined(CONFIG_MUIC_NOTIFIER) |
| if (noti) { |
| if (!muic_data->suspended) { |
| muic_notifier_detach_attached_dev( |
| muic_data->attached_dev); |
| } else { |
| muic_data->need_to_noti = true; |
| pr_info("[%s:%s] muic_data->need_to_noti = true\n", |
| MUIC_DEV_NAME, __func__); |
| } |
| } else { |
| pr_info("[%s:%s] detach Noti. for (%d) discarded.\n", |
| MUIC_DEV_NAME, __func__, muic_data->attached_dev); |
| } |
| #endif /* CONFIG_MUIC_NOTIFIER */ |
| |
| #if defined(CONFIG_MUIC_BCD_RESCAN) |
| muic_data->bc12_retry_count = 0; |
| muic_data->bc12_retry_skip = 0; |
| #endif |
| |
| muic_data->attached_dev = ATTACHED_DEV_NONE_MUIC; |
| } |
| |
| static void sm5713_muic_detect_dev(struct sm5713_muic_data *muic_data, int irq) |
| { |
| struct i2c_client *i2c = muic_data->i2c; |
| int new_dev = ATTACHED_DEV_UNKNOWN_MUIC; |
| /* struct otg_notify *o_notify = get_otg_notify(); */ |
| int intr = MUIC_INTR_DETACH; |
| int dev1 = 0, dev2 = 0, ctrl = 0, manualsw = 0, afcctrl = 0, vbvolt = 0; |
| #if defined(CONFIG_MUIC_HV_SUPPORT_POGO_DOCK) |
| int vbus_value = 0; |
| #endif |
| dev1 = sm5713_i2c_read_byte(i2c, SM5713_MUIC_REG_DEVICETYPE1); |
| dev2 = sm5713_i2c_read_byte(i2c, SM5713_MUIC_REG_DEVICETYPE2); |
| ctrl = sm5713_i2c_read_byte(i2c, SM5713_MUIC_REG_CNTL); |
| manualsw = sm5713_i2c_read_byte(i2c, SM5713_MUIC_REG_MANUAL_SW); |
| afcctrl = sm5713_i2c_read_byte(i2c, SM5713_MUIC_REG_AFCCNTL); |
| vbvolt = sm5713_i2c_read_byte(i2c, 0x3E) & 0x01; |
| |
| pr_info("[%s:%s] dev1:0x%02x, dev2:0x%02x, ctrl:0x%02x, ma:0x%02x, afcctrl:0x%02x, vbvolt:0x%02x\n", |
| MUIC_DEV_NAME, __func__, dev1, dev2, ctrl, |
| manualsw, afcctrl, vbvolt); |
| |
| #if defined(CONFIG_MUIC_HV_SUPPORT_POGO_DOCK) |
| if (gpio_is_valid(muic_data->gpio_dock)) { |
| if (gpio_get_value(muic_data->gpio_dock) == 0) { |
| intr = MUIC_INTR_ATTACH; |
| vbus_value = sm5713_get_vbus_value(muic_data); |
| if (vbus_value == 5) { |
| new_dev = ATTACHED_DEV_POGO_DOCK_5V_MUIC; |
| pr_info("[%s:%s] 5V pogo_dock attached\n", MUIC_DEV_NAME, __func__); |
| } |
| else if (vbus_value == 9) { |
| new_dev = ATTACHED_DEV_POGO_DOCK_9V_MUIC; |
| pr_info("[%s:%s] 9V pogo_dock attached\n", MUIC_DEV_NAME, __func__); |
| } |
| else { |
| new_dev = ATTACHED_DEV_POGO_DOCK_MUIC; |
| pr_info("[%s:%s] pogo_dock attached\n", MUIC_DEV_NAME, __func__); |
| } |
| } |
| if (intr == MUIC_INTR_ATTACH) { |
| /* do not check device type register */ |
| dev1 = dev2 = 0; |
| } |
| } |
| #endif /* CONFIG_MUIC_HV_SUPPORT_POGO_DOCK */ |
| |
| #if defined(CONFIG_MUIC_SUPPORT_CCIC) |
| if (muic_data->pdata->opmode == OPMODE_CCIC) { |
| switch (muic_data->ccic_info_data.ccic_evt_rid) { |
| case RID_000K: |
| intr = MUIC_INTR_ATTACH; |
| new_dev = ATTACHED_DEV_OTG_MUIC; |
| pr_info("[%s:%s] USB_OTG\n", MUIC_DEV_NAME, __func__); |
| break; |
| case RID_255K: |
| /* |
| * Don't set device type |
| * because 255K is used as Bypass mode |
| */ |
| dev1 = dev2 = 0; |
| pr_info("[%s:%s] Bypass mode(255K)\n", MUIC_DEV_NAME, |
| __func__); |
| break; |
| case RID_301K: |
| if (!(vbvolt)) |
| break; |
| intr = MUIC_INTR_ATTACH; |
| new_dev = ATTACHED_DEV_JIG_USB_ON_MUIC; |
| pr_info("[%s:%s] JIG_USB_ON(301K)\n", MUIC_DEV_NAME, |
| __func__); |
| break; |
| case RID_523K: |
| intr = MUIC_INTR_ATTACH; |
| new_dev = ATTACHED_DEV_JIG_UART_OFF_MUIC; |
| pr_info("[%s:%s] JIG_UART_OFF(523K)\n", MUIC_DEV_NAME, |
| __func__); |
| break; |
| case RID_619K: |
| intr = MUIC_INTR_ATTACH; |
| if (vbvolt) { |
| new_dev = ATTACHED_DEV_JIG_UART_ON_VB_MUIC; |
| pr_info("[%s:%s] JIG_UART_ON(619K) + VBUS\n", |
| MUIC_DEV_NAME, __func__); |
| } else { |
| new_dev = ATTACHED_DEV_JIG_UART_ON_MUIC; |
| pr_info("[%s:%s] JIG_UART_ON(619K)\n", |
| MUIC_DEV_NAME, __func__); |
| } |
| break; |
| default: |
| break; |
| } |
| |
| if (muic_data->ccic_info_data.ccic_evt_rprd) { |
| intr = MUIC_INTR_ATTACH; |
| new_dev = ATTACHED_DEV_OTG_MUIC; |
| pr_info("[%s:%s] USB_OTG\n", MUIC_DEV_NAME, __func__); |
| } else if (muic_data->ccic_info_data.ccic_evt_dcdcnt) { |
| intr = MUIC_INTR_ATTACH; |
| new_dev = ATTACHED_DEV_TIMEOUT_OPEN_MUIC; |
| pr_info("[%s:%s] CC INCOMPLETE INSERTION\n", |
| MUIC_DEV_NAME, __func__); |
| } |
| |
| if (intr == MUIC_INTR_ATTACH) { |
| /* do not check device type register */ |
| dev1 = dev2 = 0; |
| } |
| } |
| #endif |
| |
| muic_data->dev1 = dev1; |
| muic_data->dev2 = dev2; |
| |
| switch (dev1) { |
| case DEV_TYPE1_LO_TA: |
| if (!vbvolt) { |
| pr_info("[%s:%s] DEV_TYPE1_LO_TA + NO VBUS\n", |
| MUIC_DEV_NAME, __func__); |
| return; |
| } |
| intr = MUIC_INTR_ATTACH; |
| new_dev = ATTACHED_DEV_UNOFFICIAL_TA_MUIC; |
| pr_info("[%s:%s] LO_TA\n", MUIC_DEV_NAME, __func__); |
| break; |
| case DEV_TYPE1_U200: |
| if (!vbvolt) { |
| pr_info("[%s:%s] DEV_TYPE1_U200 + NO VBUS\n", |
| MUIC_DEV_NAME, __func__); |
| return; |
| } |
| intr = MUIC_INTR_ATTACH; |
| new_dev = ATTACHED_DEV_UNOFFICIAL_TA_MUIC; |
| pr_info("[%s:%s] U200\n", MUIC_DEV_NAME, __func__); |
| break; |
| case DEV_TYPE1_CDP: |
| if (!vbvolt) { |
| pr_info("[%s:%s] DEV_TYPE1_CDP + NO VBUS\n", |
| MUIC_DEV_NAME, __func__); |
| return; |
| } |
| intr = MUIC_INTR_ATTACH; |
| new_dev = ATTACHED_DEV_CDP_MUIC; |
| pr_info("[%s:%s] CDP\n", MUIC_DEV_NAME, __func__); |
| break; |
| case DEV_TYPE1_DCP: |
| case DEV_TYPE1_AFC_DCP: |
| case DEV_TYPE1_QC20_DCP: |
| if (!vbvolt) { |
| pr_info("[%s:%s] DEV_TYPE1_DCP + NO VBUS\n", |
| MUIC_DEV_NAME, __func__); |
| return; |
| } |
| intr = MUIC_INTR_ATTACH; |
| new_dev = ATTACHED_DEV_TA_MUIC; |
| pr_info("[%s:%s] DCP\n", MUIC_DEV_NAME, __func__); |
| break; |
| case DEV_TYPE1_SDP: |
| if (!vbvolt) { |
| pr_info("[%s:%s] DEV_TYPE1_SDP(USB) + NO VBUS\n", |
| MUIC_DEV_NAME, __func__); |
| return; |
| } |
| intr = MUIC_INTR_ATTACH; |
| new_dev = ATTACHED_DEV_USB_MUIC; |
| pr_info("[%s:%s] USB(SDP)\n", MUIC_DEV_NAME, __func__); |
| break; |
| case DEV_TYPE1_DCD_OUT_SDP: |
| if (!vbvolt) { |
| pr_info("[%s:%s] DEV_TYPE1_DCD_OUT_SDP + NO VBUS\n", |
| MUIC_DEV_NAME, __func__); |
| return; |
| } |
| |
| #if defined(CONFIG_MUIC_BCD_RESCAN) |
| if (irq == SM5713_MUIC_IRQ_PROBE) { |
| muic_data->bc12_retry_skip = 1; |
| intr = MUIC_INTR_ATTACH; |
| new_dev = ATTACHED_DEV_TIMEOUT_OPEN_MUIC; |
| pr_info("[%s:%s] DCD_OUT_SDP, check later\n", MUIC_DEV_NAME, __func__); |
| } else if (muic_data->ccic_afc_state == SM5713_MUIC_AFC_ABNORMAL) { |
| intr = MUIC_INTR_ATTACH; |
| new_dev = ATTACHED_DEV_TIMEOUT_OPEN_MUIC; |
| pr_info("[%s:%s] DCD_OUT_SDP\n", MUIC_DEV_NAME, __func__); |
| } else if (muic_data->bc12_retry_count < 1) { |
| muic_data->bc12_retry_count++; |
| pr_info("[%s:%s] DCD_OUT_SDP (bc12_retry_count=%d)\n", |
| MUIC_DEV_NAME, __func__, muic_data->bc12_retry_count); |
| msleep(50); |
| sm5713_muic_BCD_rescan(muic_data); |
| cancel_delayed_work(&muic_data->bc12_retry_work); |
| schedule_delayed_work(&muic_data->bc12_retry_work, |
| msecs_to_jiffies(850)); /* 850 msec */ |
| return; |
| } else { |
| intr = MUIC_INTR_ATTACH; |
| new_dev = sm5713_muic_bc12_retry_check(muic_data); |
| } |
| #else |
| intr = MUIC_INTR_ATTACH; |
| new_dev = ATTACHED_DEV_TIMEOUT_OPEN_MUIC; |
| pr_info("[%s:%s] DCD_OUT_SDP\n", MUIC_DEV_NAME, __func__); |
| #endif |
| break; |
| |
| default: |
| break; |
| } |
| |
| switch (dev2) { |
| case DEV_TYPE2_USB_OTG: |
| intr = MUIC_INTR_ATTACH; |
| new_dev = ATTACHED_DEV_OTG_MUIC; |
| pr_info("[%s:%s] USB_OTG\n", MUIC_DEV_NAME, __func__); |
| break; |
| case DEV_TYPE2_JIG_UART_OFF: |
| /* W/A: sm5713 can't recognize VBUS without CC attach */ |
| if (vbvolt == 0) |
| vbvolt = (sm5713_get_vbus_value(muic_data) >= 4) |
| ? 0x01 : 0; |
| intr = MUIC_INTR_ATTACH; |
| |
| if (vbvolt) { |
| new_dev = ATTACHED_DEV_JIG_UART_OFF_VB_MUIC; |
| pr_info("[%s:%s] JIG_UART_OFF(523K) + VBUS\n", |
| MUIC_DEV_NAME, __func__); |
| } else { |
| new_dev = ATTACHED_DEV_JIG_UART_OFF_MUIC; |
| pr_info("[%s:%s] JIG_UART_OFF(523K)\n", |
| MUIC_DEV_NAME, __func__); |
| } |
| |
| break; |
| case DEV_TYPE2_JIG_UART_ON: |
| intr = MUIC_INTR_ATTACH; |
| new_dev = ATTACHED_DEV_JIG_UART_ON_MUIC; |
| pr_info("[%s:%s] JIG_UART_ON(619K)\n", |
| MUIC_DEV_NAME, __func__); |
| break; |
| case DEV_TYPE2_JIG_USB_OFF: |
| if (!vbvolt) |
| break; |
| intr = MUIC_INTR_ATTACH; |
| new_dev = ATTACHED_DEV_JIG_USB_OFF_MUIC; |
| pr_info("[%s:%s] JIG_USB_OFF(255K)\n", MUIC_DEV_NAME, __func__); |
| break; |
| case DEV_TYPE2_JIG_USB_ON: |
| /* W/A: sm5713 can't recognize VBUS without CC attach */ |
| if (vbvolt == 0) |
| vbvolt = (sm5713_get_vbus_value(muic_data) >= 4) |
| ? 0x01 : 0; |
| if (!vbvolt) |
| break; |
| intr = MUIC_INTR_ATTACH; |
| new_dev = ATTACHED_DEV_JIG_USB_ON_MUIC; |
| pr_info("[%s:%s] JIG_USB_ON(301K)\n", MUIC_DEV_NAME, __func__); |
| break; |
| |
| default: |
| break; |
| } |
| |
| if (intr == MUIC_INTR_ATTACH) |
| sm5713_muic_handle_attach(muic_data, new_dev, vbvolt, irq); |
| else |
| sm5713_muic_handle_detach(muic_data, irq); |
| } |
| |
| static int sm5713_muic_reg_init(struct sm5713_muic_data *muic_data) |
| { |
| struct i2c_client *i2c = muic_data->i2c; |
| int ret = 0; |
| int intmask1 = 0, intmask2 = 0, cntl = 0, manualsw = 0; |
| int dev1 = 0, dev2 = 0, afccntl = 0, afctxd = 0; |
| int afcstatus = 0, vbus1 = 0, vbus2 = 0; |
| int cfg1 = 0; |
| int afccntl2 = 0; |
| |
| pr_info("[%s:%s]\n", MUIC_DEV_NAME, __func__); |
| |
| sm5713_i2c_write_byte(i2c, SM5713_MUIC_REG_CNTL, 0x24); |
| sm5713_i2c_write_byte(i2c, SM5713_MUIC_REG_AFCCNTL, 0x00); |
| |
| intmask1 = sm5713_i2c_read_byte(i2c, SM5713_MUIC_REG_INTMASK1); |
| intmask2 = sm5713_i2c_read_byte(i2c, SM5713_MUIC_REG_INTMASK2); |
| cntl = sm5713_i2c_read_byte(i2c, SM5713_MUIC_REG_CNTL); |
| manualsw = sm5713_i2c_read_byte(i2c, SM5713_MUIC_REG_MANUAL_SW); |
| |
| dev1 = sm5713_i2c_read_byte(i2c, SM5713_MUIC_REG_DEVICETYPE1); |
| dev2 = sm5713_i2c_read_byte(i2c, SM5713_MUIC_REG_DEVICETYPE2); |
| afccntl = sm5713_i2c_read_byte(i2c, SM5713_MUIC_REG_AFCCNTL); |
| afctxd = sm5713_i2c_read_byte(i2c, SM5713_MUIC_REG_AFCTXD); |
| |
| afcstatus = sm5713_i2c_read_byte(i2c, SM5713_MUIC_REG_AFCSTATUS); |
| vbus1 = sm5713_i2c_read_byte(i2c, SM5713_MUIC_REG_VBUS_VOLTAGE1); |
| vbus2 = sm5713_i2c_read_byte(i2c, SM5713_MUIC_REG_VBUS_VOLTAGE2); |
| |
| cfg1 = sm5713_i2c_read_byte(i2c, SM5713_MUIC_REG_CFG1); |
| |
| afccntl2 = sm5713_i2c_read_byte(i2c, SM5713_MUIC_REG_AFC_CNTL2); |
| afccntl2 = afccntl2 | 0x80; |
| sm5713_i2c_write_byte(i2c, SM5713_MUIC_REG_AFC_CNTL2, afccntl2); |
| |
| /* set dcd timer out to 0.8s */ |
| cntl &= ~CTRL_DCDTIMER_MASK; |
| #ifdef CONFIG_MUIC_SM5713_BC1_2_CERTI |
| cntl |= (CTRL_DCDTIMER_MASK & 0x01); |
| #else |
| cntl |= (CTRL_DCDTIMER_MASK & 0x10); |
| #endif |
| sm5713_i2c_write_byte(i2c, SM5713_MUIC_REG_CNTL, cntl); |
| |
| pr_info("[%s:%s] intmask1:0x%x, intmask2:0x%x, cntl:0x%x, mansw:0x%x\n", |
| MUIC_DEV_NAME, __func__, intmask1, intmask2, cntl, manualsw); |
| pr_info("[%s:%s] dev1:0x%x, dev2:0x%x, afccntl:0x%x, afctxd:0x%x\n", |
| MUIC_DEV_NAME, __func__, dev1, dev2, afccntl, afctxd); |
| pr_info("[%s:%s] afcstatus:0x%x, vbus1:0x%x, vbus2:0x%x\n", |
| MUIC_DEV_NAME, __func__, afcstatus, vbus1, vbus2); |
| |
| pr_info("[%s:%s] cfg1:0x%x, afccntl2=0x%x\n", |
| MUIC_DEV_NAME, __func__, cfg1, afccntl2); |
| |
| return ret; |
| } |
| |
| #if defined(CONFIG_MUIC_HV_SUPPORT_POGO_DOCK) |
| static irqreturn_t sm5713_muic_pogo_irq_thread(int irq, void *data) |
| { |
| struct sm5713_muic_data *muic_data = data; |
| struct i2c_client *i2c = muic_data->i2c; |
| u8 val = 0, before = 0; |
| int ret = 0; |
| |
| pr_info("[%s:%s]\n", MUIC_DEV_NAME, __func__); |
| |
| if (gpio_is_valid(muic_data->gpio_dock)) { |
| if (gpio_get_value(muic_data->gpio_dock) == 0) { |
| before = sm5713_i2c_read_byte(i2c, SM5713_MUIC_REG_CFG1); |
| val = before | (0x1 << 5); /* bc12 without cc attach */ |
| ret = sm5713_i2c_guaranteed_wbyte(i2c, SM5713_MUIC_REG_CFG1, |
| val); |
| if (ret < 0) |
| pr_err("[%s:%s] err write(%d)\n", |
| MUIC_DEV_NAME, __func__, ret); |
| |
| val = sm5713_i2c_read_byte(i2c, SM5713_MUIC_REG_CFG1); |
| pr_info("[%s:%s] change CFG1:%02x -> %02x\n", |
| MUIC_DEV_NAME, __func__, before, val); |
| } |
| else { |
| before = sm5713_i2c_read_byte(i2c, SM5713_MUIC_REG_CFG1); |
| val = before & ~(0x1 << 5); /* bc12 with cc attach */ |
| ret = sm5713_i2c_guaranteed_wbyte(i2c, SM5713_MUIC_REG_CFG1, |
| val); |
| if (ret < 0) |
| pr_err("[%s:%s] err write(%d)\n", |
| MUIC_DEV_NAME, __func__, ret); |
| val = sm5713_i2c_read_byte(i2c, SM5713_MUIC_REG_CFG1); |
| pr_info("[%s:%s] change CFG1:%02x -> %02x\n", |
| MUIC_DEV_NAME, __func__, before, val); |
| } |
| } |
| |
| return IRQ_HANDLED; |
| } |
| #endif |
| |
| static irqreturn_t sm5713_muic_irq_thread(int irq, void *data) |
| { |
| struct sm5713_muic_data *muic_data = data; |
| /* struct i2c_client *i2c = muic_data->i2c; */ |
| /* u8 reg_data = 0; */ |
| int irq_num = irq - muic_data->mfd_pdata->irq_base; |
| /* int reg_val, vbvolt, adc, adc_recheck = 0; */ |
| /* int i = 0; */ |
| |
| mutex_lock(&muic_data->muic_mutex); |
| wake_lock(&muic_data->wake_lock); |
| |
| |
| if (irq != (SM5713_MUIC_IRQ_PROBE)) { |
| pr_info("[%s:%s] irq_gpio(%d), irq (%d), irq_num(%d:%s)\n", |
| MUIC_DEV_NAME, __func__, |
| muic_data->mfd_pdata->irq_base, irq, irq_num, |
| SM5713_MUIC_INT_NAME[irq_num]); |
| } else { |
| pr_info("[%s:%s] irq_gpio(%d), irq (%d), irq_num(%d)\n", |
| MUIC_DEV_NAME, __func__, |
| muic_data->mfd_pdata->irq_base, irq, irq_num); |
| } |
| |
| if (irq_num == SM5713_MUIC_IRQ_INT1_VBUS_RID_DETACH) { |
| pr_info("%s: afc_retry_work(INT1_DETACH) cancel\n", __func__); |
| cancel_delayed_work(&muic_data->afc_retry_work); |
| cancel_delayed_work(&muic_data->afc_torch_work); |
| cancel_delayed_work(&muic_data->afc_prepare_work); |
| #if defined(CONFIG_MUIC_SUPPORT_CCIC) |
| cancel_delayed_work(&muic_data->ccic_afc_work); |
| #endif |
| #if defined(CONFIG_MUIC_BCD_RESCAN) |
| cancel_delayed_work(&muic_data->bc12_retry_work); |
| #endif |
| } |
| |
| if (irq_num == SM5713_MUIC_IRQ_INT2_AFC_ERROR) { |
| sm5713_afc_error(muic_data); |
| goto EOH; |
| } |
| |
| if (irq_num == SM5713_MUIC_IRQ_INT2_AFC_STA_CHG) { |
| sm5713_afc_sta_chg(muic_data); |
| goto EOH; |
| } |
| |
| if (irq_num == SM5713_MUIC_IRQ_INT2_MULTI_BYTE) { |
| sm5713_afc_multi_byte(muic_data); |
| goto EOH; |
| } |
| |
| if (irq_num == SM5713_MUIC_IRQ_INT2_VBUS_UPDATE) { |
| sm5713_afc_vbus_update(muic_data); |
| goto EOH; |
| } |
| |
| if (irq_num == SM5713_MUIC_IRQ_INT2_AFC_ACCEPTED) { |
| sm5713_afc_ta_accept(muic_data); |
| goto EOH; |
| } |
| |
| if (irq_num == SM5713_MUIC_IRQ_INT2_AFC_TA_ATTACHED) { |
| sm5713_afc_ta_attach(muic_data); |
| goto EOH; |
| } |
| |
| if ((irq == (SM5713_MUIC_IRQ_PROBE)) || |
| #if defined(CONFIG_MUIC_HV_SUPPORT_POGO_DOCK) |
| (irq == muic_data->irq_dock) || |
| #endif |
| (irq_num == SM5713_MUIC_IRQ_INT1_DPDM_OVP) || |
| (irq_num == SM5713_MUIC_IRQ_INT1_VBUS_RID_DETACH) || |
| (irq_num == SM5713_MUIC_IRQ_INT1_AUTOVBUSCHECK) || |
| (irq_num == SM5713_MUIC_IRQ_INT1_RID_DETECT) || |
| (irq_num == SM5713_MUIC_IRQ_INT1_CHGTYPE) || |
| (irq_num == SM5713_MUIC_IRQ_INT1_DCDTIMEOUT)) { |
| |
| sm5713_muic_detect_dev(muic_data, irq); |
| } |
| |
| EOH: |
| wake_unlock(&muic_data->wake_lock); |
| mutex_unlock(&muic_data->muic_mutex); |
| |
| pr_info("[%s:%s] done\n", MUIC_DEV_NAME, __func__); |
| return IRQ_HANDLED; |
| } |
| |
| static void sm5713_muic_handle_event(struct work_struct *work) |
| { |
| struct sm5713_muic_data *muic_data = container_of(work, |
| struct sm5713_muic_data, muic_event_work); |
| |
| pr_info("[%s:%s]\n", MUIC_DEV_NAME, __func__); |
| |
| mutex_lock(&muic_data->muic_mutex); |
| wake_lock(&muic_data->wake_lock); |
| sm5713_muic_detect_dev(muic_data, SM5713_MUIC_IRQ_WORK); |
| wake_unlock(&muic_data->wake_lock); |
| mutex_unlock(&muic_data->muic_mutex); |
| } |
| |
| #if defined(CONFIG_VBUS_NOTIFIER) |
| static int sm5713_muic_handle_vbus_notification(struct notifier_block *nb, |
| unsigned long action, void *data) |
| { |
| struct sm5713_muic_data *muic_data = container_of(nb, |
| struct sm5713_muic_data, vbus_nb); |
| int vbus_type = *(int *)data; |
| |
| muic_data->vbus_state = vbus_type; |
| |
| if (muic_data->is_water_detect) { |
| pr_info("[%s:%s] vbus(%d) water(%d)\n", MUIC_DEV_NAME, __func__, |
| vbus_type, muic_data->is_water_detect); |
| |
| switch (vbus_type) { |
| case STATUS_VBUS_HIGH: |
| muic_set_hiccup_mode(1); |
| break; |
| case STATUS_VBUS_LOW: |
| break; |
| default: |
| break; |
| } |
| } |
| |
| return 0; |
| } |
| #endif /* CONFIG_VBUS_NOTIFIER */ |
| |
| #if 0 |
| static void sm5713_dcd_rescan(struct sm5713_muic_data *muic_data) |
| { |
| struct i2c_client *i2c = muic_data->i2c; |
| int ctrl = 0; |
| int bcd_rescan = 0; |
| |
| |
| mutex_lock(&muic_data->switch_mutex); |
| pr_info("[%s:%s] call\n", MUIC_DEV_NAME, __func__); |
| |
| ctrl = sm5713_i2c_read_byte(i2c, SM5713_MUIC_REG_CNTL); |
| pr_info("[%s:%s] CONTROL: 0x%x\n", MUIC_DEV_NAME, __func__, ctrl); |
| |
| bcd_rescan = sm5713_i2c_read_byte(i2c, SM5713_MUIC_REG_REVID2); |
| bcd_rescan = bcd_rescan | 0x10; |
| sm5713_i2c_write_byte(i2c, SM5713_MUIC_REG_REVID2, bcd_rescan); |
| |
| bcd_rescan = bcd_rescan & 0xEF; |
| sm5713_i2c_write_byte(i2c, SM5713_MUIC_REG_REVID2, bcd_rescan); |
| |
| mutex_unlock(&muic_data->switch_mutex); |
| } |
| #endif |
| |
| #if defined(CONFIG_HICCUP_CHARGER) |
| static int sm5713_muic_ccic_set_hiccup_mode(int val) |
| { |
| struct sm5713_muic_data *muic_data = static_data; |
| |
| if (static_data == NULL) |
| return -ENODEV; |
| |
| if (lpcharge) |
| return 0; |
| |
| pr_info("[%s:%s] val = %d\n", MUIC_DEV_NAME, __func__, val); |
| |
| if (val == 1) { /* Hiccup mode on */ |
| switch_to_ap_audio(muic_data); |
| } else { /* Hiccup mode off */ |
| com_to_open(muic_data); |
| } |
| |
| return 0; |
| } |
| #endif |
| |
| static int sm5713_muic_hv_charger_init(void) |
| { |
| struct sm5713_muic_data *muic_data = static_data; |
| |
| #if defined(CONFIG_VBUS_NOTIFIER) |
| /* enable hiccup mode after charger init*/ |
| if (muic_data->is_water_detect && |
| muic_data->vbus_state == STATUS_VBUS_HIGH) |
| muic_set_hiccup_mode(1); |
| #endif /* CONFIG_VBUS_NOTIFIER */ |
| |
| return sm5713_muic_charger_init(); |
| } |
| |
| static void sm5713_muic_debug_reg_log(struct work_struct *work) |
| { |
| struct sm5713_muic_data *muic_data = container_of(work, |
| struct sm5713_muic_data, debug_work.work); |
| struct i2c_client *i2c = muic_data->i2c; |
| u8 data[9] = {0, }; |
| |
| data[0] = sm5713_i2c_read_byte(i2c, SM5713_MUIC_REG_DEVICETYPE1); |
| data[1] = sm5713_i2c_read_byte(i2c, SM5713_MUIC_REG_DEVICETYPE2); |
| data[2] = sm5713_i2c_read_byte(i2c, SM5713_MUIC_REG_CNTL); |
| data[3] = sm5713_i2c_read_byte(i2c, SM5713_MUIC_REG_MANUAL_SW); |
| data[4] = sm5713_i2c_read_byte(i2c, SM5713_MUIC_REG_AFCCNTL); |
| data[5] = sm5713_i2c_read_byte(i2c, SM5713_MUIC_REG_AFCTXD); |
| data[6] = sm5713_i2c_read_byte(i2c, SM5713_MUIC_REG_AFCSTATUS); |
| data[7] = sm5713_i2c_read_byte(i2c, SM5713_MUIC_REG_VBUS_VOLTAGE1); |
| data[8] = sm5713_i2c_read_byte(i2c, SM5713_MUIC_REG_VBUS_VOLTAGE2); |
| |
| pr_info("%s DEV_TYPE[0x%02x 0x%02x] CNTL:0x%02x MAN_SW:0x%02x AFC_CNTL:0x%02x AFC_TXD:0x%02x AFC_STATUS:0x%02x VBUS_VOL[0x%02x 0x%02x] attached_dev:%d\n", |
| __func__, data[0], data[1], data[2], data[3], data[4], |
| data[5], data[6], data[7], data[8], |
| muic_data->attached_dev); |
| |
| if (!muic_data->suspended) |
| schedule_delayed_work(&muic_data->debug_work, |
| msecs_to_jiffies(60000)); |
| } |
| |
| static int sm5713_init_rev_info(struct sm5713_muic_data *muic_data) |
| { |
| u8 dev_id = 0; |
| int ret = 0; |
| |
| pr_info("[%s:%s]\n", MUIC_DEV_NAME, __func__); |
| |
| dev_id = sm5713_i2c_read_byte(muic_data->i2c, SM5713_MUIC_REG_DeviceID); |
| if (dev_id < 0) { |
| pr_err("[%s:%s] dev_id(%d)\n", MUIC_DEV_NAME, __func__, dev_id); |
| ret = -ENODEV; |
| } else { |
| muic_data->muic_vendor = (dev_id & 0x07); |
| muic_data->muic_version = 0x00; |
| pr_info("[%s:%s] vendor=0x%x, ver=0x%x, dev_id=0x%x\n", |
| MUIC_DEV_NAME, __func__, muic_data->muic_vendor, |
| muic_data->muic_version, dev_id); |
| } |
| return ret; |
| } |
| |
| #define REQUEST_IRQ(_irq, _dev_id, _name) \ |
| do { \ |
| ret = request_threaded_irq(_irq, NULL, sm5713_muic_irq_thread, \ |
| 0, _name, _dev_id); \ |
| if (ret < 0) { \ |
| pr_err("[%s:%s] Failed to request IRQ #%d: %d\n", \ |
| MUIC_DEV_NAME, __func__, _irq, ret); \ |
| _irq = 0; \ |
| } \ |
| } while (0) |
| |
| static int sm5713_muic_irq_init(struct sm5713_muic_data *muic_data) |
| { |
| int ret = 0; |
| |
| if (muic_data->mfd_pdata && (muic_data->mfd_pdata->irq_base > 0)) { |
| int irq_base = muic_data->mfd_pdata->irq_base; |
| |
| /* request MUIC IRQ */ |
| muic_data->irqs.irq_dpdm_ovp = |
| irq_base + SM5713_MUIC_IRQ_INT1_DPDM_OVP; |
| REQUEST_IRQ(muic_data->irqs.irq_dpdm_ovp, muic_data, |
| "muic-dpdm_ovp"); |
| |
| muic_data->irqs.irq_vbus_rid_detach = |
| irq_base + SM5713_MUIC_IRQ_INT1_VBUS_RID_DETACH; |
| REQUEST_IRQ(muic_data->irqs.irq_vbus_rid_detach, muic_data, |
| "muic-vbus_rid_detach"); |
| |
| muic_data->irqs.irq_autovbus_check = |
| irq_base + SM5713_MUIC_IRQ_INT1_AUTOVBUSCHECK; |
| REQUEST_IRQ(muic_data->irqs.irq_autovbus_check, muic_data, |
| "muic-autovbus_check"); |
| |
| muic_data->irqs.irq_rid_detect = |
| irq_base + SM5713_MUIC_IRQ_INT1_RID_DETECT; |
| REQUEST_IRQ(muic_data->irqs.irq_rid_detect, muic_data, |
| "muic-rid_detect"); |
| |
| muic_data->irqs.irq_chgtype_attach = |
| irq_base + SM5713_MUIC_IRQ_INT1_CHGTYPE; |
| REQUEST_IRQ(muic_data->irqs.irq_chgtype_attach, muic_data, |
| "muic-chgtype_attach"); |
| |
| muic_data->irqs.irq_dectimeout = |
| irq_base + SM5713_MUIC_IRQ_INT1_DCDTIMEOUT; |
| REQUEST_IRQ(muic_data->irqs.irq_dectimeout, muic_data, |
| "muic-dectimeout"); |
| |
| muic_data->irqs.irq_afc_error = |
| irq_base + SM5713_MUIC_IRQ_INT2_AFC_ERROR; |
| REQUEST_IRQ(muic_data->irqs.irq_afc_error, muic_data, |
| "muic-afc_error"); |
| |
| muic_data->irqs.irq_afc_sta_chg = |
| irq_base + SM5713_MUIC_IRQ_INT2_AFC_STA_CHG; |
| REQUEST_IRQ(muic_data->irqs.irq_afc_sta_chg, muic_data, |
| "muic-afc_sta_chg"); |
| |
| muic_data->irqs.irq_multi_byte = |
| irq_base + SM5713_MUIC_IRQ_INT2_MULTI_BYTE; |
| REQUEST_IRQ(muic_data->irqs.irq_multi_byte, muic_data, |
| "muic-multi_byte"); |
| |
| muic_data->irqs.irq_vbus_update = |
| irq_base + SM5713_MUIC_IRQ_INT2_VBUS_UPDATE; |
| REQUEST_IRQ(muic_data->irqs.irq_vbus_update, muic_data, |
| "muic-vbus_update"); |
| |
| muic_data->irqs.irq_afc_accepted = |
| irq_base + SM5713_MUIC_IRQ_INT2_AFC_ACCEPTED; |
| REQUEST_IRQ(muic_data->irqs.irq_afc_accepted, muic_data, |
| "muic-afc_accepted"); |
| |
| muic_data->irqs.irq_afc_ta_attached = |
| irq_base + SM5713_MUIC_IRQ_INT2_AFC_TA_ATTACHED; |
| REQUEST_IRQ(muic_data->irqs.irq_afc_ta_attached, muic_data, |
| "muic-afc_ta_attached"); |
| |
| } |
| |
| pr_info("[%s:%s] muic-dpdm_ovp(%d), muic-vbus_rid_detach(%d), muic-autovbus_check(%d), muic-rid_detect(%d)", |
| MUIC_DEV_NAME, __func__, muic_data->irqs.irq_dpdm_ovp, |
| muic_data->irqs.irq_vbus_rid_detach, |
| muic_data->irqs.irq_autovbus_check, |
| muic_data->irqs.irq_rid_detect); |
| |
| pr_info("[%s:%s] muic-chgtype_attach(%d), muic-dectimeout(%d), muic-afc_error(%d), muic-afc_sta_chg(%d)\n", |
| MUIC_DEV_NAME, __func__, muic_data->irqs.irq_chgtype_attach, |
| muic_data->irqs.irq_dectimeout, muic_data->irqs.irq_afc_error, |
| muic_data->irqs.irq_afc_sta_chg); |
| |
| pr_info("[%s:%s] muic-multi_byte(%d), muic-vbus_update(%d), muic-afc_accepted(%d), muic-afc_ta_attached(%d)\n", |
| MUIC_DEV_NAME, __func__, muic_data->irqs.irq_multi_byte, |
| muic_data->irqs.irq_vbus_update, |
| muic_data->irqs.irq_afc_accepted, |
| muic_data->irqs.irq_afc_ta_attached); |
| |
| return ret; |
| } |
| |
| #define FREE_IRQ(_irq, _dev_id, _name) \ |
| do { \ |
| if (_irq) { \ |
| free_irq(_irq, _dev_id); \ |
| pr_info("[%s:%s] IRQ(%d):%s free done\n", MUIC_DEV_NAME,\ |
| __func__, _irq, _name); \ |
| } \ |
| } while (0) |
| |
| static void sm5713_muic_free_irqs(struct sm5713_muic_data *muic_data) |
| { |
| pr_info("[%s:%s]\n", MUIC_DEV_NAME, __func__); |
| |
| /* free MUIC IRQ */ |
| FREE_IRQ(muic_data->irqs.irq_dpdm_ovp, muic_data, "muic-dpdm_ovp"); |
| FREE_IRQ(muic_data->irqs.irq_vbus_rid_detach, muic_data, |
| "muic-vbus_rid_detach"); |
| FREE_IRQ(muic_data->irqs.irq_autovbus_check, muic_data, |
| "muic-autovbus_check"); |
| FREE_IRQ(muic_data->irqs.irq_rid_detect, muic_data, "muic-rid_detect"); |
| FREE_IRQ(muic_data->irqs.irq_chgtype_attach, muic_data, |
| "muic-chgtype_attach"); |
| FREE_IRQ(muic_data->irqs.irq_dectimeout, muic_data, "muic-dectimeout"); |
| |
| FREE_IRQ(muic_data->irqs.irq_afc_error, muic_data, "muic-afc_error"); |
| FREE_IRQ(muic_data->irqs.irq_afc_sta_chg, muic_data, |
| "muic-afc_sta_chg"); |
| FREE_IRQ(muic_data->irqs.irq_multi_byte, muic_data, "muic-multi_byte"); |
| FREE_IRQ(muic_data->irqs.irq_vbus_update, muic_data, |
| "muic-vbus_update"); |
| FREE_IRQ(muic_data->irqs.irq_afc_accepted, muic_data, |
| "muic-afc_accepted"); |
| FREE_IRQ(muic_data->irqs.irq_afc_ta_attached, muic_data, |
| "muic-afc_ta_attached"); |
| |
| } |
| |
| #if defined(CONFIG_OF) |
| static int of_sm5713_muic_dt(struct device *dev, |
| struct sm5713_muic_data *muic_data) |
| { |
| struct device_node *np, *np_muic; |
| int ret = 0; |
| #if defined(CONFIG_MUIC_HV_SUPPORT_POGO_DOCK) |
| int pogo_gpio; |
| #endif |
| |
| pr_info("[%s:%s]\n", MUIC_DEV_NAME, __func__); |
| |
| np = dev->parent->of_node; |
| if (!np) { |
| pr_err("[%s:%s] could not find np\n", MUIC_DEV_NAME, __func__); |
| return -ENODEV; |
| } |
| |
| np_muic = of_find_node_by_name(np, "muic"); |
| if (!np_muic) { |
| pr_err("[%s:%s] could not find muic sub-node np_muic\n", |
| MUIC_DEV_NAME, __func__); |
| return -EINVAL; |
| } |
| |
| #if defined(CONFIG_MUIC_HV_SUPPORT_POGO_DOCK) |
| pogo_gpio = of_get_named_gpio(np_muic, "muic,dock_int_ap", 0); |
| muic_data->gpio_dock = pogo_gpio; |
| if (gpio_is_valid(muic_data->gpio_dock)) |
| pr_info("%s:%s dock_int_ap:%d, value:%d\n", MUIC_DEV_NAME, __func__, |
| muic_data->gpio_dock, gpio_get_value(muic_data->gpio_dock)); |
| else |
| pr_err("%s:%s dock_int_ap is invalid\n", MUIC_DEV_NAME, __func__); |
| |
| pogo_gpio = gpio_to_irq(pogo_gpio); |
| if (pogo_gpio < 0) { |
| pr_err("%s: fail to return irq corresponding gpio\n", __func__); |
| return -EINVAL; |
| } |
| muic_data->irq_dock = pogo_gpio; |
| #endif /* CONFIG_MUIC_HV_SUPPORT_POGO_DOCK */ |
| |
| return ret; |
| } |
| #endif /* CONFIG_OF */ |
| |
| /* if need to set sm5713 pdata */ |
| static const struct of_device_id sm5713_muic_match_table[] = { |
| { .compatible = "sm5713-muic",}, |
| {}, |
| }; |
| |
| static int sm5713_muic_probe(struct platform_device *pdev) |
| { |
| struct sm5713_dev *sm5713 = dev_get_drvdata(pdev->dev.parent); |
| struct sm5713_platform_data *mfd_pdata = dev_get_platdata(sm5713->dev); |
| #if defined(CONFIG_IF_CB_MANAGER) |
| struct muic_dev *muic_d; |
| #endif |
| struct sm5713_muic_data *muic_data; |
| int ret = 0; |
| |
| pr_info("[%s:%s]\n", MUIC_DEV_NAME, __func__); |
| |
| muic_data = kzalloc(sizeof(struct sm5713_muic_data), GFP_KERNEL); |
| if (!muic_data) { |
| ret = -ENOMEM; |
| goto err_return; |
| } |
| |
| if (!mfd_pdata) { |
| pr_err("[%s:%s] failed to get sm5713 mfd platform data\n", |
| MUIC_DEV_NAME, __func__); |
| ret = -ENOMEM; |
| goto err_kfree1; |
| } |
| |
| /* save platfom data for gpio control functions */ |
| static_data = muic_data; |
| muic_data->pdata = &muic_pdata; |
| muic_data->muic_data = muic_data; |
| |
| #if defined(CONFIG_IF_CB_MANAGER) |
| muic_d = kzalloc(sizeof(struct muic_dev), GFP_KERNEL); |
| if (!muic_d) { |
| ret = -ENOMEM; |
| goto err_kfree1; |
| } |
| |
| muic_d->ops = &ops_muic; |
| muic_d->data = (void *)muic_data; |
| muic_data->man = register_muic(muic_d); |
| #endif |
| |
| #if defined(CONFIG_MUIC_SUPPORT_CCIC) |
| muic_data->pdata->opmode = get_ccic_info() & 0xF; |
| muic_data->ccic_afc_state = SM5713_MUIC_AFC_NORMAL; |
| muic_data->ccic_afc_state_count = 0; |
| muic_data->need_to_path_open = false; |
| #endif |
| |
| muic_data->sm5713_dev = sm5713; |
| muic_data->dev = &pdev->dev; |
| muic_data->i2c = sm5713->muic; |
| muic_data->mfd_pdata = mfd_pdata; |
| |
| muic_data->is_water_detect = false; |
| muic_data->afc_irq_disabled = false; |
| muic_data->fled_torch_enable = false; |
| muic_data->fled_flash_enable = false; |
| muic_data->old_afctxd = 0x00; |
| muic_data->hv_voltage = 0; |
| |
| #if defined(CONFIG_HICCUP_CHARGER) |
| muic_data->is_hiccup_mode = false; |
| muic_data->pdata->muic_set_hiccup_mode_cb = |
| sm5713_muic_ccic_set_hiccup_mode; |
| #endif |
| |
| #if defined(CONFIG_MUIC_BCD_RESCAN) |
| muic_data->bc12_retry_count = 0; |
| muic_data->bc12_retry_skip = 0; |
| INIT_DELAYED_WORK(&muic_data->bc12_retry_work, muic_bc12_retry_work); |
| #endif |
| |
| #if defined(CONFIG_OF) |
| ret = of_sm5713_muic_dt(&pdev->dev, muic_data); |
| if (ret < 0) |
| pr_err("[%s:%s] no muic dt! ret[%d]\n", |
| MUIC_DEV_NAME, __func__, ret); |
| #endif /* CONFIG_OF */ |
| |
| |
| muic_data->is_factory_start = false; |
| muic_data->attached_dev = ATTACHED_DEV_UNKNOWN_MUIC; |
| |
| platform_set_drvdata(pdev, muic_data); |
| |
| mutex_init(&muic_data->switch_mutex); |
| mutex_init(&muic_data->muic_mutex); |
| mutex_init(&muic_data->afc_mutex); |
| |
| wake_lock_init(&muic_data->wake_lock, WAKE_LOCK_SUSPEND, "muic_wake"); |
| |
| if (muic_data->pdata->init_gpio_cb) |
| ret = muic_data->pdata->init_gpio_cb(NULL, get_switch_sel()); |
| if (ret) { |
| pr_err("[%s:%s] failed to init gpio(%d)\n", |
| MUIC_DEV_NAME, __func__, ret); |
| goto fail_init_gpio; |
| } |
| |
| #ifdef CONFIG_DRV_SAMSUNG |
| /* create sysfs group */ |
| ret = sysfs_create_group(&switch_device->kobj, &sm5713_muic_group); |
| if (ret) { |
| pr_err("[%s:%s] failed to create sysfs\n", |
| MUIC_DEV_NAME, __func__); |
| goto fail; |
| } |
| dev_set_drvdata(switch_device, muic_data); |
| #endif |
| |
| ret = sm5713_init_rev_info(muic_data); |
| if (ret) { |
| pr_err("[%s:%s] failed to init muic(%d)\n", |
| MUIC_DEV_NAME, __func__, ret); |
| goto fail; |
| } |
| |
| ret = sm5713_muic_reg_init(muic_data); |
| if (ret) { |
| pr_err("[%s:%s] failed to init muic(%d)\n", |
| MUIC_DEV_NAME, __func__, ret); |
| goto fail; |
| } |
| |
| sm5713_hv_muic_initialize(muic_data); |
| |
| |
| muic_data->is_rustproof = muic_data->pdata->rustproof_on; |
| if (muic_data->is_rustproof) { |
| pr_err("[%s:%s] rustproof is enabled\n", |
| MUIC_DEV_NAME, __func__); |
| com_to_open(muic_data); |
| } |
| |
| |
| if (get_afc_mode() == CH_MODE_AFC_DISABLE_VAL) { |
| pr_info("[%s:%s] AFC mode disabled\n", MUIC_DEV_NAME, __func__); |
| muic_data->pdata->afc_disable = true; |
| } else { |
| pr_info("[%s:%s] AFC mode enabled\n", MUIC_DEV_NAME, __func__); |
| muic_data->pdata->afc_disable = false; |
| } |
| |
| muic_data->pdata->muic_afc_set_voltage_cb = sm5713_muic_afc_set_voltage; |
| muic_data->pdata->muic_hv_charger_init_cb = sm5713_muic_hv_charger_init; |
| |
| if (muic_data->pdata->init_switch_dev_cb) |
| muic_data->pdata->init_switch_dev_cb(); |
| |
| /* initial cable detection */ |
| sm5713_muic_irq_thread(SM5713_MUIC_IRQ_PROBE, muic_data); |
| #if defined(CONFIG_MUIC_HV_SUPPORT_POGO_DOCK) |
| ret = request_threaded_irq(muic_data->irq_dock, NULL, sm5713_muic_pogo_irq_thread, |
| IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, |
| "pogo_dock", muic_data); |
| if (ret < 0) { |
| pr_err("[%s:%s] failed to request_threaded_irq pogo\n", MUIC_DEV_NAME, __func__); |
| goto fail; |
| } |
| #endif |
| |
| ret = sm5713_muic_irq_init(muic_data); |
| if (ret) { |
| pr_err("[%s:%s] failed to init irq(%d)\n", |
| MUIC_DEV_NAME, __func__, ret); |
| goto fail_init_irq; |
| } |
| |
| #if defined(CONFIG_VBUS_NOTIFIER) |
| vbus_notifier_register(&muic_data->vbus_nb, |
| sm5713_muic_handle_vbus_notification, |
| VBUS_NOTIFY_DEV_MUIC); |
| muic_data->vbus_state = STATUS_VBUS_UNKNOWN; |
| #endif |
| #if defined(CONFIG_MUIC_SUPPORT_CCIC) |
| if (muic_data->pdata->opmode & OPMODE_CCIC) |
| sm5713_muic_register_ccic_notifier(muic_data); |
| else |
| pr_info("[%s:%s] OPMODE_MUIC, CCIC NOTIFIER is not used.\n", |
| MUIC_DEV_NAME, __func__); |
| #endif |
| |
| INIT_WORK(&(muic_data->muic_event_work), sm5713_muic_handle_event); |
| INIT_DELAYED_WORK(&muic_data->debug_work, sm5713_muic_debug_reg_log); |
| schedule_delayed_work(&muic_data->debug_work, msecs_to_jiffies(10000)); |
| |
| return 0; |
| |
| fail_init_irq: |
| sm5713_muic_free_irqs(muic_data); |
| fail: |
| #ifdef CONFIG_DRV_SAMSUNG |
| sysfs_remove_group(&switch_device->kobj, &sm5713_muic_group); |
| #endif |
| mutex_destroy(&muic_data->switch_mutex); |
| mutex_destroy(&muic_data->muic_mutex); |
| mutex_destroy(&muic_data->afc_mutex); |
| |
| fail_init_gpio: |
| #if defined(CONFIG_IF_CB_MANAGER) |
| kfree(muic_d); |
| #endif |
| err_kfree1: |
| kfree(muic_data); |
| err_return: |
| return ret; |
| } |
| |
| static int sm5713_muic_remove(struct platform_device *pdev) |
| { |
| struct sm5713_muic_data *muic_data = platform_get_drvdata(pdev); |
| #ifdef CONFIG_DRV_SAMSUNG |
| sysfs_remove_group(&switch_device->kobj, &sm5713_muic_group); |
| #endif |
| |
| if (muic_data) { |
| pr_info("[%s:%s]\n", MUIC_DEV_NAME, __func__); |
| |
| cancel_delayed_work_sync(&muic_data->debug_work); |
| disable_irq_wake(muic_data->i2c->irq); |
| sm5713_muic_free_irqs(muic_data); |
| #if defined(CONFIG_VBUS_NOTIFIER) |
| vbus_notifier_unregister(&muic_data->vbus_nb); |
| #endif |
| mutex_destroy(&muic_data->switch_mutex); |
| mutex_destroy(&muic_data->muic_mutex); |
| mutex_destroy(&muic_data->afc_mutex); |
| i2c_set_clientdata(muic_data->i2c, NULL); |
| #if defined(CONFIG_IF_CB_MANAGER) |
| kfree(muic_data->man->muic_d); |
| #endif |
| kfree(muic_data); |
| } |
| |
| return 0; |
| } |
| |
| static void sm5713_muic_shutdown(struct platform_device *pdev) |
| { |
| struct sm5713_muic_data *muic_data = platform_get_drvdata(pdev); |
| int ret; |
| |
| cancel_delayed_work_sync(&muic_data->debug_work); |
| |
| pr_info("[%s:%s]\n", MUIC_DEV_NAME, __func__); |
| if (!muic_data->i2c) { |
| pr_err("[%s:%s] no muic i2c client\n", MUIC_DEV_NAME, __func__); |
| return; |
| } |
| |
| pr_info("[%s:%s] open D+,D-,V_bus line\n", MUIC_DEV_NAME, __func__); |
| ret = com_to_open(muic_data); |
| if (ret < 0) |
| pr_err("[%s:%s] fail to open mansw\n", MUIC_DEV_NAME, __func__); |
| |
| /* set auto sw mode before shutdown to make sure device goes into */ |
| /* LPM charging when TA or USB is connected during off state */ |
| pr_info("[%s:%s] muic auto detection enable\n", |
| MUIC_DEV_NAME, __func__); |
| /* false(0):auto true(1):manual */ |
| ret = set_manual_sw(muic_data, false); |
| if (ret < 0) { |
| pr_err("[%s:%s] fail to update reg\n", MUIC_DEV_NAME, __func__); |
| return; |
| } |
| } |
| |
| #if defined CONFIG_PM |
| static int sm5713_muic_suspend(struct device *dev) |
| { |
| struct sm5713_muic_data *muic_data = dev_get_drvdata(dev); |
| |
| muic_data->suspended = true; |
| cancel_delayed_work_sync(&muic_data->debug_work); |
| |
| return 0; |
| } |
| |
| static int sm5713_muic_resume(struct device *dev) |
| { |
| struct sm5713_muic_data *muic_data = dev_get_drvdata(dev); |
| |
| muic_data->suspended = false; |
| |
| if (muic_data->need_to_noti) { |
| if (muic_data->attached_dev) |
| muic_notifier_attach_attached_dev( |
| muic_data->attached_dev); |
| else |
| muic_notifier_detach_attached_dev( |
| muic_data->attached_dev); |
| muic_data->need_to_noti = false; |
| } |
| schedule_delayed_work(&muic_data->debug_work, msecs_to_jiffies(1500)); |
| |
| return 0; |
| } |
| #else |
| #define sm5713_muic_suspend NULL |
| #define sm5713_muic_resume NULL |
| #endif |
| |
| static SIMPLE_DEV_PM_OPS(sm5713_muic_pm_ops, sm5713_muic_suspend, |
| sm5713_muic_resume); |
| |
| static struct platform_driver sm5713_muic_driver = { |
| .driver = { |
| .name = "sm5713-muic", |
| .owner = THIS_MODULE, |
| .of_match_table = sm5713_muic_match_table, |
| #ifdef CONFIG_PM |
| .pm = &sm5713_muic_pm_ops, |
| #endif |
| }, |
| .probe = sm5713_muic_probe, |
| /* FIXME: It makes build error of defined but not used. */ |
| .remove = sm5713_muic_remove, |
| .shutdown = sm5713_muic_shutdown, |
| }; |
| |
| static int __init sm5713_muic_init(void) |
| { |
| return platform_driver_register(&sm5713_muic_driver); |
| } |
| late_initcall(sm5713_muic_init); |
| |
| static void __exit sm5713_muic_exit(void) |
| { |
| platform_driver_unregister(&sm5713_muic_driver); |
| } |
| module_exit(sm5713_muic_exit); |
| |
| MODULE_DESCRIPTION("SM5713 USB Switch driver"); |
| MODULE_LICENSE("GPL"); |