From: Kisang Lee Date: Thu, 17 May 2018 11:52:26 +0000 (+0900) Subject: [COMMON] usb: notify: add related USB notify driver X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=0fd7b5c3dc93923d7f8401a9b0f83f8747c51755;p=GitHub%2FLineageOS%2Fandroid_kernel_motorola_exynos9610.git [COMMON] usb: notify: add related USB notify driver Change-Id: I0aee0a5f9e2c300a1cb788a6b7fe5be6c01ccad9 Signed-off-by: Kisang Lee --- diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 72eb3e41e3b6..cc20214e6b93 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -117,6 +117,10 @@ source "drivers/usb/mtu3/Kconfig" source "drivers/usb/musb/Kconfig" +source "drivers/usb/notify/Kconfig" + +source "drivers/usb/manager/Kconfig" + source "drivers/usb/dwc3/Kconfig" source "drivers/usb/dwc2/Kconfig" diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 060643a1b5c8..dad5bd3effc9 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -65,3 +65,5 @@ obj-$(CONFIG_USB_COMMON) += common/ obj-$(CONFIG_USBIP_CORE) += usbip/ obj-$(CONFIG_TYPEC) += typec/ +obj-$(CONFIG_USB_NOTIFY_LAYER) += notify/ +obj-$(CONFIG_USB_TYPEC_MANAGER_NOTIFIER) += manager/ diff --git a/drivers/usb/manager/Kconfig b/drivers/usb/manager/Kconfig new file mode 100644 index 000000000000..7bdcfa3dee93 --- /dev/null +++ b/drivers/usb/manager/Kconfig @@ -0,0 +1,11 @@ +# +# USB TypeC Manager driver +# + +comment "USB TypeC Manager configs" + +config USB_TYPEC_MANAGER_NOTIFIER + bool "USB TypeC Manager driver" + help + If you say yes here you will get support for + USB TypeC Manager function diff --git a/drivers/usb/manager/Makefile b/drivers/usb/manager/Makefile new file mode 100644 index 000000000000..fcc6409d5869 --- /dev/null +++ b/drivers/usb/manager/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for usb typec manager drivers +# + +obj-$(CONFIG_USB_TYPEC_MANAGER_NOTIFIER) += usb_typec_manager_notifier.o +#obj-$(CONFIG_USB_TYPEC_MANAGER) += usb_typec_manager.o diff --git a/drivers/usb/manager/usb_typec_manager_notifier.c b/drivers/usb/manager/usb_typec_manager_notifier.c new file mode 100644 index 000000000000..9e84f9780f68 --- /dev/null +++ b/drivers/usb/manager/usb_typec_manager_notifier.c @@ -0,0 +1,1114 @@ +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#if defined(CONFIG_VBUS_NOTIFIER) +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEBUG +#define SET_MANAGER_NOTIFIER_BLOCK(nb, fn, dev) do { \ + (nb)->notifier_call = (fn); \ + (nb)->priority = (dev); \ + } while (0) + +#define DESTROY_MANAGER_NOTIFIER_BLOCK(nb) \ + SET_MANAGER_NOTIFIER_BLOCK(nb, NULL, -1) + +typedef enum +{ + VBUS_NOTIFIER, + CCIC_NOTIFIER, + MUIC_NOTIFIER +}notifier_register; + +static int manager_notifier_init_done = 0; +static int confirm_manager_notifier_register = 0; +static int manager_notifier_init(void); + +struct device *manager_device; +extern struct device *ccic_device; +manager_data_t typec_manager; +void set_usb_enumeration_state(int state); +static void cable_type_check_work(bool state, int time); +void calc_duration_time(unsigned long sTime, unsigned long eTime, unsigned long *dTime); +void wVbus_time_update(int mode); +void water_dry_time_update(int mode); + +static int manager_notifier_notify(void *data) +{ + MANAGER_NOTI_TYPEDEF manager_noti = *(MANAGER_NOTI_TYPEDEF *)data; + int ret = 0; + + pr_info("usb: [M] %s: src:%s dest:%s id:%s " + "sub1:%02x sub2:%02x sub3:%02x\n", __func__, + (manager_noti.src 86400)) + calcDtime = 0; + + *dTime += calcDtime; +} + +void manager_notifier_usbdp_support(void) +{ + + MANAGER_NOTI_TYPEDEF m_noti; + if( typec_manager.dp_check_done == 1 ) { + m_noti.src = CCIC_NOTIFY_DEV_MANAGER; + m_noti.dest = CCIC_NOTIFY_DEV_USB_DP; + m_noti.id = CCIC_NOTIFY_ID_USB_DP; + m_noti.sub1 = typec_manager.dp_is_connect; + m_noti.sub2 = typec_manager.dp_hs_connect; + m_noti.sub3 = 0; + m_noti.pd = NULL; + manager_notifier_notify(&m_noti); + typec_manager.dp_check_done = 0; + } + return; +} + +static void cable_type_check(struct work_struct *work) +{ + CC_NOTI_USB_STATUS_TYPEDEF p_usb_noti; + CC_NOTI_ATTACH_TYPEDEF p_batt_noti; + + if ( (typec_manager.ccic_drp_state != USB_STATUS_NOTIFY_ATTACH_UFP) || + typec_manager.is_UFPS ){ + pr_info("usb: [M] %s: skip case\n", __func__); + return; + } + + pr_info("usb: [M] %s: usb=%d, pd=%d\n", __func__, typec_manager.usb_enum_state, typec_manager.pd_con_state); + if(!typec_manager.usb_enum_state || + (typec_manager.muic_data_refresh + && typec_manager.cable_type==MANAGER_NOTIFY_MUIC_CHARGER)) { + + /* TA cable Type */ + p_usb_noti.src = CCIC_NOTIFY_DEV_MANAGER; + p_usb_noti.dest = CCIC_NOTIFY_DEV_USB; + p_usb_noti.id = CCIC_NOTIFY_ID_USB; + p_usb_noti.attach = CCIC_NOTIFY_DETACH; + p_usb_noti.drp = USB_STATUS_NOTIFY_DETACH; + p_usb_noti.sub3 = 0; + p_usb_noti.pd = NULL; + manager_notifier_notify(&p_usb_noti); + + } else { + /* USB cable Type */ + p_batt_noti.src = CCIC_NOTIFY_DEV_MANAGER; + p_batt_noti.dest = CCIC_NOTIFY_DEV_BATTERY; + p_batt_noti.id = CCIC_NOTIFY_ID_USB; + p_batt_noti.attach = 0; + p_batt_noti.rprd = 0; + p_batt_noti.cable_type = PD_USB_TYPE; + p_batt_noti.pd = NULL; + manager_notifier_notify(&p_batt_noti); + } +} + +static void cable_type_check_work(bool state, int time) { + if(typec_manager.usb_enable_state) { + cancel_delayed_work_sync(&typec_manager.cable_check_work); + if(state) { + schedule_delayed_work(&typec_manager.cable_check_work, msecs_to_jiffies(time*100)); + } + } +} + +static void muic_work_without_ccic(struct work_struct *work) +{ + CC_NOTI_USB_STATUS_TYPEDEF p_usb_noti; + + pr_info("usb: [M] %s: working state=%d, vbus=%s\n", __func__, + typec_manager.muic_attach_state_without_ccic, + typec_manager.vbus_state == STATUS_VBUS_HIGH ? "HIGH" : "LOW"); + + if (typec_manager.muic_attach_state_without_ccic) { + switch (typec_manager.muic_action) { + case MUIC_NOTIFY_CMD_ATTACH: +#if defined(CONFIG_VBUS_NOTIFIER) + if(typec_manager.vbus_state == STATUS_VBUS_HIGH) +#endif + { + p_usb_noti.src = CCIC_NOTIFY_DEV_MUIC; + p_usb_noti.dest = CCIC_NOTIFY_DEV_USB; + p_usb_noti.id = CCIC_NOTIFY_ID_USB; + p_usb_noti.attach = CCIC_NOTIFY_ATTACH; + p_usb_noti.drp = USB_STATUS_NOTIFY_ATTACH_UFP; + p_usb_noti.sub3 = 0; + p_usb_noti.pd = NULL; + manager_notifier_notify(&p_usb_noti); + typec_manager.ccic_drp_state = USB_STATUS_NOTIFY_ATTACH_UFP; + } + break; + case MUIC_NOTIFY_CMD_DETACH: + typec_manager.muic_attach_state_without_ccic = 0; + p_usb_noti.src = CCIC_NOTIFY_DEV_MUIC; + p_usb_noti.dest = CCIC_NOTIFY_DEV_USB; + p_usb_noti.id = CCIC_NOTIFY_ID_USB; + p_usb_noti.attach = CCIC_NOTIFY_DETACH; + p_usb_noti.drp = USB_STATUS_NOTIFY_DETACH; + p_usb_noti.sub3 = 0; + p_usb_noti.pd = NULL; + manager_notifier_notify(&p_usb_noti); + typec_manager.ccic_drp_state = USB_STATUS_NOTIFY_DETACH; + break; + default : + break; + } + } +} + +void water_dry_time_update(int mode) +{ + struct timeval time; + struct rtc_time det_time; + static int rtc_update_check = 1; + + do_gettimeofday(&time); + if (rtc_update_check) { + rtc_update_check = 0; + rtc_time_to_tm(time.tv_sec, &det_time); + pr_info("%s: year=%d\n", __func__, det_time.tm_year); + if (det_time.tm_year == 70) { /* (1970-01-01 00:00:00) */ + schedule_delayed_work(&typec_manager.rtctime_update_work, msecs_to_jiffies(5000)); + } + } + + if (mode) { + /* WATER */ + typec_manager.waterDet_time = time.tv_sec; + } else { + /* DRY */ + typec_manager.dryDet_time = time.tv_sec; + calc_duration_time(typec_manager.waterDet_time, + typec_manager.dryDet_time, &typec_manager.waterDet_duration); + } +} + +static void water_det_rtc_time_update(struct work_struct *work) +{ + struct timeval time; + struct rtc_time rtctime; + static int max_retry = 1; + + do_gettimeofday(&time); + rtc_time_to_tm(time.tv_sec, &rtctime); + if ((rtctime.tm_year == 70) && (max_retry<5)) { + /* (1970-01-01 00:00:00) */ + if (typec_manager.wVbus_det) { + calc_duration_time(typec_manager.wVbusHigh_time, + time.tv_sec, &typec_manager.wVbus_duration); + typec_manager.wVbusHigh_time = time.tv_sec; + } + max_retry++; + schedule_delayed_work(&typec_manager.rtctime_update_work, msecs_to_jiffies(5000)); + } else { + if (typec_manager.water_det) { + typec_manager.waterDet_time = time.tv_sec; + typec_manager.waterDet_duration += max_retry*5; + if (typec_manager.wVbus_det) { + typec_manager.wVbusHigh_time = time.tv_sec; + typec_manager.wVbus_duration += 5; + } + } + } +} + +void wVbus_time_update(int mode) +{ + struct timeval time; + + do_gettimeofday(&time); + + if (mode) { + /* WVBUS HIGH */ + typec_manager.wVbusHigh_time = time.tv_sec; + } else { + /* WVBUS LOW */ + typec_manager.wVbusLow_time = time.tv_sec; + calc_duration_time(typec_manager.wVbusHigh_time, + typec_manager.wVbusLow_time, &typec_manager.wVbus_duration); + } +} + +#if defined(CONFIG_VBUS_NOTIFIER) +void handle_muic_fake_event(int event) +{ + + if (typec_manager.muic_fake_event_wq_processing) { + typec_manager.muic_fake_event_wq_processing = 0; + cancel_delayed_work_sync(&typec_manager.vbus_noti_work); + } + + switch (event) { + case EVENT_CANCEL: + typec_manager.muic_attach_state_without_ccic = 0; + break; + case EVENT_LOAD: + if(typec_manager.muic_attach_state_without_ccic + || typec_manager.ccic_drp_state == USB_STATUS_NOTIFY_ATTACH_UFP) { + schedule_delayed_work(&typec_manager.vbus_noti_work, msecs_to_jiffies(1000)); + typec_manager.muic_fake_event_wq_processing = 1; + } + break; + default : + break; + } +} + + +static void muic_fake_event_work(struct work_struct *work) +{ + CC_NOTI_ATTACH_TYPEDEF muic_noti; + + pr_info("usb: [M] %s: drp=%d, rid=%d, without_ccic=%d\n", __func__, + typec_manager.ccic_drp_state, + typec_manager.ccic_rid_state, + typec_manager.muic_attach_state_without_ccic); + + typec_manager.muic_fake_event_wq_processing = 0; + + if( typec_manager.ccic_rid_state == RID_523K || typec_manager.ccic_rid_state == RID_619K + || typec_manager.ccic_drp_state == USB_STATUS_NOTIFY_ATTACH_DFP + || typec_manager.vbus_state == STATUS_VBUS_HIGH) { + return; + } else if (typec_manager.muic_action == MUIC_NOTIFY_CMD_DETACH) { + typec_manager.muic_attach_state_without_ccic = 1; + schedule_delayed_work(&typec_manager.muic_noti_work, msecs_to_jiffies(0)); + return; + } + + typec_manager.muic_attach_state_without_ccic = 1; + muic_noti.src = CCIC_NOTIFY_DEV_MANAGER; + muic_noti.dest = CCIC_NOTIFY_DEV_MUIC; + muic_noti.id = CCIC_NOTIFY_ID_ATTACH; + muic_noti.attach = 0; + muic_noti.rprd = 0; + muic_noti.cable_type = typec_manager.muic_cable_type; + muic_noti.pd = NULL; + manager_notifier_notify(&muic_noti); +} +#endif + +static int manager_handle_ccic_notification(struct notifier_block *nb, + unsigned long action, void *data) +{ + MANAGER_NOTI_TYPEDEF p_noti = *(MANAGER_NOTI_TYPEDEF *)data; + CC_NOTI_ATTACH_TYPEDEF bat_noti; + CC_NOTI_ATTACH_TYPEDEF muic_noti; + int ret = 0; + + pr_info("usb: [M] %s: src:%s dest:%s id:%s attach/rid:%d\n", __func__, + (p_noti.srcnotifier_call(nb, m_noti.id, &(m_noti)); + + } else if(listener == MANAGER_NOTIFY_CCIC_USB) { + /* CC_NOTI_USB_STATUS_TYPEDEF */ + m_noti.src = CCIC_NOTIFY_DEV_MANAGER; + m_noti.dest = CCIC_NOTIFY_DEV_USB; + m_noti.id = CCIC_NOTIFY_ID_USB; + if (typec_manager.water_det) + m_noti.sub1 = 0; + else + m_noti.sub1 = typec_manager.ccic_attach_state || typec_manager.muic_action; + + if (m_noti.sub1) { + if (typec_manager.ccic_drp_state && + (typec_manager.cable_type != MANAGER_NOTIFY_MUIC_CHARGER)) { + m_noti.sub2 = typec_manager.ccic_drp_state; + } else if (typec_manager.cable_type == MANAGER_NOTIFY_MUIC_USB) { + typec_manager.ccic_drp_state = USB_STATUS_NOTIFY_ATTACH_UFP; + m_noti.sub2 = USB_STATUS_NOTIFY_ATTACH_UFP; + } else { + typec_manager.ccic_drp_state = USB_STATUS_NOTIFY_DETACH; + m_noti.sub2 = USB_STATUS_NOTIFY_DETACH; + } + } else { + m_noti.sub2 = USB_STATUS_NOTIFY_DETACH; + } + m_noti.sub3 = 0; + pr_info("usb: [M] %s USB: attach=%d, drp=%s \n", __func__, m_noti.sub1, + CCIC_NOTI_USB_STATUS_Print[m_noti.sub2]); + nb->notifier_call(nb, m_noti.id, &(m_noti)); + alternate_mode_start_wait |= 0x1; + if (alternate_mode_start_wait == 0x11) { + pr_info("usb: [M] %s USB & DP driver is registered! Alternate mode Start!\n", __func__); +#if defined(CONFIG_CCIC_ALTERNATE_MODE) + if (pccic_data && pccic_data->set_enable_alternate_mode) + pccic_data->set_enable_alternate_mode(ALTERNATE_MODE_READY | ALTERNATE_MODE_START); +#endif + } + } else if(listener == MANAGER_NOTIFY_CCIC_DP) { + m_noti.src = CCIC_NOTIFY_DEV_MANAGER; + m_noti.dest = CCIC_NOTIFY_DEV_DP; + m_noti.sub2 = 0; + m_noti.sub3 = 0; + if (typec_manager.dp_attach_state == CCIC_NOTIFY_ATTACH) { + m_noti.id = CCIC_NOTIFY_ID_DP_CONNECT; + m_noti.sub1 = typec_manager.dp_attach_state; + nb->notifier_call(nb, m_noti.id, &(m_noti)); + + m_noti.id = CCIC_NOTIFY_ID_DP_LINK_CONF; + m_noti.sub1 = typec_manager.dp_cable_type; + nb->notifier_call(nb, m_noti.id, &(m_noti)); + + if (typec_manager.dp_hpd_state == CCIC_NOTIFY_HIGH) { + m_noti.id = CCIC_NOTIFY_ID_DP_HPD; + m_noti.sub1 = typec_manager.dp_hpd_state; + nb->notifier_call(nb, m_noti.id, &(m_noti)); + } + } + alternate_mode_start_wait |= 0x10; + if (alternate_mode_start_wait == 0x11) { + pr_info("usb: [M] %s USB & DP driver is registered! Alternate mode Start!\n", __func__); +#if defined(CONFIG_CCIC_ALTERNATE_MODE) + if (pccic_data && pccic_data->set_enable_alternate_mode) + pccic_data->set_enable_alternate_mode(ALTERNATE_MODE_READY | ALTERNATE_MODE_START); +#endif + } + } + + return ret; +} + +int manager_notifier_unregister(struct notifier_block *nb) +{ + int ret = 0; + pr_info("usb: [M] %s: listener=%d unregister\n", __func__, nb->priority); + + if (nb->priority == MANAGER_NOTIFY_CCIC_MUIC) { + ret = blocking_notifier_chain_unregister(&(typec_manager.manager_muic_notifier), nb); + if (ret < 0) + pr_err("usb: [M] %s: muic blocking_notifier_chain_unregister error(%d)\n", + __func__, ret); + DESTROY_MANAGER_NOTIFIER_BLOCK(nb); + } else { + ret = blocking_notifier_chain_unregister(&(typec_manager.manager_ccic_notifier), nb); + if (ret < 0) + pr_err("usb: [M] %s: ccic blocking_notifier_chain_unregister error(%d)\n", + __func__, ret); + DESTROY_MANAGER_NOTIFIER_BLOCK(nb); + } + return ret; +} + +static void delayed_manger_notifier_init(struct work_struct *work) +{ + int ret = 0; + int notifier_result = 0; + static int retry_count = 1; + int max_retry_count = 5; + + pr_info("%s : %d = times!\n",__func__,retry_count); +#if defined(CONFIG_VBUS_NOTIFIER) + if(confirm_manager_notifier_register & (1 << VBUS_NOTIFIER)) + { + ret = vbus_notifier_register(&typec_manager.vbus_nb, manager_handle_vbus_notification,VBUS_NOTIFY_DEV_MANAGER); + if(ret) + notifier_result |= (1 << VBUS_NOTIFIER); + } +#endif + if(confirm_manager_notifier_register & (1 << CCIC_NOTIFIER)) + { + ret = ccic_notifier_register(&typec_manager.ccic_nb, manager_handle_ccic_notification,CCIC_NOTIFY_DEV_MANAGER); + if(ret) + notifier_result |= (1 << CCIC_NOTIFIER); + } + + if(confirm_manager_notifier_register & (1 << MUIC_NOTIFIER)) + { + ret = muic_notifier_register(&typec_manager.muic_nb, manager_handle_muic_notification,MUIC_NOTIFY_DEV_MANAGER); + if(ret) + notifier_result |= (1 << MUIC_NOTIFIER); + } + + confirm_manager_notifier_register = notifier_result; + pr_info("%s : result of register = %d!\n",__func__, confirm_manager_notifier_register); + + if(confirm_manager_notifier_register) + { + pr_err("Manager notifier init time is %d.\n",retry_count); + if(retry_count++ != max_retry_count) + schedule_delayed_work(&typec_manager.manager_init_work, msecs_to_jiffies(2000)); + else + pr_err("fail to init manager notifier\n"); + } + else + { + pr_info("%s : done!\n",__func__); + } +} + + +static int manager_notifier_init(void) +{ + int ret = 0; + int notifier_result = 0; + pccic_data_t pccic_data; + + pr_info("usb: [M] %s\n", __func__); + ccic_notifier_init(); + pccic_data = dev_get_drvdata(ccic_device); + + if(manager_notifier_init_done) + { + pr_err("%s already registered\n", __func__); + goto out; + } + manager_notifier_init_done = 1; + + manager_device = sec_device_create(NULL, "typec_manager"); + if (IS_ERR(manager_device)) { + pr_err("usb: [M] %s Failed to create device(switch)!\n", __func__); + ret = -ENODEV; + goto out; + } + + typec_manager.ccic_attach_state = CCIC_NOTIFY_DETACH; + typec_manager.ccic_drp_state = USB_STATUS_NOTIFY_DETACH; + typec_manager.muic_action = MUIC_NOTIFY_CMD_DETACH; + typec_manager.muic_cable_type = ATTACHED_DEV_NONE_MUIC; + typec_manager.cable_type = MANAGER_NOTIFY_MUIC_NONE; + typec_manager.muic_data_refresh = 0; + typec_manager.usb_enum_state = 0; + typec_manager.water_det = 0; + typec_manager.wVbus_det = 0; + typec_manager.water_count =0; + typec_manager.dry_count = 0; + typec_manager.usb210_count = 0; + typec_manager.usb310_count = 0; + typec_manager.waterChg_count = 0; + typec_manager.waterDet_duration = 0; + typec_manager.wVbus_duration = 0; + typec_manager.dp_is_connect = 0; + typec_manager.dp_hs_connect = 0; + typec_manager.dp_check_done = 1; + typec_manager.muic_attach_state_without_ccic = 0; +#if defined(CONFIG_VBUS_NOTIFIER) + typec_manager.muic_fake_event_wq_processing = 0; +#endif + typec_manager.vbus_state = 0; + typec_manager.is_UFPS = 0; + typec_manager.ccic_rid_state = RID_UNDEFINED; + typec_manager.pd = NULL; + + BLOCKING_INIT_NOTIFIER_HEAD(&(typec_manager.manager_ccic_notifier)); + BLOCKING_INIT_NOTIFIER_HEAD(&(typec_manager.manager_muic_notifier)); + + INIT_DELAYED_WORK(&typec_manager.manager_init_work, + delayed_manger_notifier_init); + + INIT_DELAYED_WORK(&typec_manager.cable_check_work, + cable_type_check); + + INIT_DELAYED_WORK(&typec_manager.muic_noti_work, + muic_work_without_ccic); + + INIT_DELAYED_WORK(&typec_manager.rtctime_update_work, + water_det_rtc_time_update); + +#if defined(CONFIG_CCIC_ALTERNATE_MODE) + if (pccic_data && pccic_data->set_enable_alternate_mode) + pccic_data->set_enable_alternate_mode(ALTERNATE_MODE_NOT_READY); +#endif +#if defined(CONFIG_VBUS_NOTIFIER) + INIT_DELAYED_WORK(&typec_manager.vbus_noti_work, + muic_fake_event_work); +#endif + + // Register manager handler to ccic notifier block list +#if defined(CONFIG_VBUS_NOTIFIER) + ret = vbus_notifier_register(&typec_manager.vbus_nb, manager_handle_vbus_notification,VBUS_NOTIFY_DEV_MANAGER); + if(ret) + notifier_result |= (1 << VBUS_NOTIFIER); +#endif + ret = ccic_notifier_register(&typec_manager.ccic_nb, manager_handle_ccic_notification,CCIC_NOTIFY_DEV_MANAGER); + if(ret) + notifier_result |= (1 << CCIC_NOTIFIER); + ret = muic_notifier_register(&typec_manager.muic_nb, manager_handle_muic_notification,MUIC_NOTIFY_DEV_MANAGER); + if(ret) + notifier_result |= (1 << MUIC_NOTIFIER); + + confirm_manager_notifier_register = notifier_result; + pr_info("%s : result of register = %d!\n",__func__, confirm_manager_notifier_register); + + if(confirm_manager_notifier_register) + { + schedule_delayed_work(&typec_manager.manager_init_work, msecs_to_jiffies(2000)); + } + else + { + pr_info("%s : done!\n",__func__); + } + + pr_info("usb: [M] %s end\n", __func__); +out: + return ret; +} + +static void __exit manager_notifier_exit(void) +{ + pr_info("usb: [M] %s exit\n", __func__); +#if defined(CONFIG_VBUS_NOTIFIER) + vbus_notifier_unregister(&typec_manager.vbus_nb); +#endif + ccic_notifier_unregister(&typec_manager.ccic_nb); + muic_notifier_unregister(&typec_manager.muic_nb); +} + +late_initcall(manager_notifier_init); +module_exit(manager_notifier_exit); + diff --git a/drivers/usb/notify/Kconfig b/drivers/usb/notify/Kconfig new file mode 100644 index 000000000000..44dac2fc77d0 --- /dev/null +++ b/drivers/usb/notify/Kconfig @@ -0,0 +1,84 @@ +# +# USB notify configuration +# + +comment "USB Notify features" +config USB_HOST_NOTIFY + boolean "USB Host notify Driver" + depends on USB_NOTIFY_LAYER + help + Android framework needs uevents for usb host operation. + Host notify Driver serves uevent format + that is used by usb host or otg. + +config USB_NOTIFY_LAYER + boolean "USB notify layer" + depends on USB + help + Added usb notify layer on gadget,host,otg drivers. + The usb notify layer controls otg mode and vbus draw + and vbus detect irq. + +config USB_NOTIFIER + boolean "USB notifier" + depends on USB_NOTIFY_LAYER + help + Added platform driver to call usb notify. + The usb_notifier.c can be fixed for each models. + +config USB_DEBUG_DETAILED_LOG + boolean "USB detailed log for debugging" + depends on USB + help + Add detailed log for debugging. + +config USB_STORAGE_DETECT + boolean "USB storage detect function" + depends on USB && SCSI + depends on USB_STORAGE + help + This feature enables to detect inserting or removing + sd card in sd reader device. + This must to be defined in /usb/storage/Kconfig + directory originally. But this feature is defined + in this Kconfig to gather samsung feature about usb host. + +config USB_HMT_SAMSUNG_INPUT + boolean "USB HMT inputs for samsung hmt devices" + depends on HID + help + Some samsung smart phones support gear vr. + The samsung gear vr need some special hid input codes. + This feature enables special hid input codes. + +config USB_EXTERNAL_NOTIFY + boolean "USB external notify" + depends on USB_NOTIFY_LAYER + help + Added usb external notify. + The usb external notify broadcast special situation + to muic and charger. + +config USB_NOTIFY_PROC_LOG + boolean "USB proc log" + depends on USB_NOTIFY_LAYER + depends on USB_HOST_NOTIFY + help + Added proc usblog. + The USB STATE, The USB MODE, Cable event + are saved in usblog heap. + +config USB_HOST_SAMSUNG_FEATURE + boolean "USB Host Samsung Feature" + depends on USB + help + USB Host Samsung Feature. + It is different from LSI BSP code. + +config USB_HW_PARAM + boolean "USB H/W Parameter" + depends on USB_NOTIFY_LAYER + depends on USB_HOST_NOTIFY + help + Added USB H/W Parameter for DQA Agent. + You need more works for USB PDIC driver. diff --git a/drivers/usb/notify/Makefile b/drivers/usb/notify/Makefile new file mode 100644 index 000000000000..8da21a7cddce --- /dev/null +++ b/drivers/usb/notify/Makefile @@ -0,0 +1,9 @@ + +# usb notify driver +obj-$(CONFIG_USB_HOST_NOTIFY) += host_notify_class.o +obj-$(CONFIG_USB_EXTERNAL_NOTIFY) += external_notify.o +obj-$(CONFIG_USB_NOTIFY_PROC_LOG) += usblog_proc_notify.o +obj-$(CONFIG_USB_NOTIFY_LAYER) += usb_notify_layer.o +usb_notify_layer-y := usb_notify.o usb_notify_sysfs.o dock_notify.o +obj-$(CONFIG_USB_NOTIFIER) += usb_notifier.o + diff --git a/drivers/usb/notify/dock_notify.c b/drivers/usb/notify/dock_notify.c new file mode 100644 index 000000000000..e9bb93d14c13 --- /dev/null +++ b/drivers/usb/notify/dock_notify.c @@ -0,0 +1,407 @@ +/* + * Copyright (C) 2015-2017 Samsung Electronics Co. Ltd. + * + * 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. + */ + + /* usb notify layer v3.1 */ + +#define pr_fmt(fmt) "usb_notify: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include "../core/hub.h" + +#define SMARTDOCK_INDEX 1 +#define MMDOCK_INDEX 2 + +struct dev_table { + struct usb_device_id dev; + int index; +}; + +static struct dev_table enable_notify_hub_table[] = { + { .dev = { USB_DEVICE(0x0424, 0x2514), }, + .index = SMARTDOCK_INDEX, + }, /* SMART DOCK HUB 1 */ + { .dev = { USB_DEVICE(0x1a40, 0x0101), }, + .index = SMARTDOCK_INDEX, + }, /* SMART DOCK HUB 2 */ + { .dev = { USB_DEVICE(0x0424, 0x9512), }, + .index = MMDOCK_INDEX, + }, /* SMSC USB LAN HUB 9512 */ + {} +}; + +static struct dev_table essential_device_table[] = { + { .dev = { USB_DEVICE(0x08bb, 0x2704), }, + .index = SMARTDOCK_INDEX, + }, /* TI USB Audio DAC 1 */ + { .dev = { USB_DEVICE(0x08bb, 0x27c4), }, + .index = SMARTDOCK_INDEX, + }, /* TI USB Audio DAC 2 */ + { .dev = { USB_DEVICE(0x0424, 0xec00), }, + .index = MMDOCK_INDEX, + }, /* SMSC LAN Driver */ + {} +}; + +static struct dev_table update_autotimer_device_table[] = { + { .dev = { USB_DEVICE(0x04e8, 0xa500), }, + .index = 5, /* 5 sec timer */ + }, /* GearVR1 */ + { .dev = { USB_DEVICE(0x04e8, 0xa501), }, + .index = 5, + }, /* GearVR2 */ + { .dev = { USB_DEVICE(0x04e8, 0xa502), }, + .index = 5, + }, /* GearVR3 */ + {} +}; + +static int check_essential_device(struct usb_device *dev, int index) +{ + struct dev_table *id; + int ret = 0; + + /* check VID, PID */ + for (id = essential_device_table; id->dev.match_flags; id++) { + if ((id->dev.match_flags & USB_DEVICE_ID_MATCH_VENDOR) && + (id->dev.match_flags & USB_DEVICE_ID_MATCH_PRODUCT) && + id->dev.idVendor == le16_to_cpu(dev->descriptor.idVendor) && + id->dev.idProduct == le16_to_cpu(dev->descriptor.idProduct) && + id->index == index) { + ret = 1; + break; + } + } + return ret; +} + +static int check_gamepad_device(struct usb_device *dev) +{ + int ret = 0; + + pr_info("%s : product=%s\n", __func__, dev->product); + + if (!dev->product) + return ret; + + if (!strncmp(dev->product, "Gamepad for SAMSUNG", 19)) + ret = 1; + + return ret; +} + +static int check_lanhub_device(struct usb_device *dev) +{ + int ret = 0; + + pr_info("%s : product=%s\n", __func__, dev->product); + + if (!dev->product) + return ret; + + if (!strncmp(dev->product, "LAN9512", 8)) + ret = 1; + + return ret; +} + +static int is_notify_hub(struct usb_device *dev) +{ + struct dev_table *id; + struct usb_device *hdev; + int ret = 0; + + hdev = dev->parent; + if (!hdev) + goto skip; + /* check VID, PID */ + for (id = enable_notify_hub_table; id->dev.match_flags; id++) { + if ((id->dev.match_flags & USB_DEVICE_ID_MATCH_VENDOR) && + (id->dev.match_flags & USB_DEVICE_ID_MATCH_PRODUCT) && + id->dev.idVendor == le16_to_cpu(hdev->descriptor.idVendor) && + id->dev.idProduct == le16_to_cpu(hdev->descriptor.idProduct)) { + ret = (hdev->parent && + (hdev->parent == dev->bus->root_hub)) ? id->index : 0; + break; + } + } +skip: + return ret; +} + +static int get_autosuspend_time(struct usb_device *dev) +{ + struct dev_table *id; + int ret = 0; + + /* check VID, PID */ + for (id = update_autotimer_device_table; id->dev.match_flags; id++) { + if ((id->dev.match_flags & USB_DEVICE_ID_MATCH_VENDOR) && + (id->dev.match_flags & USB_DEVICE_ID_MATCH_PRODUCT) && + id->dev.idVendor == le16_to_cpu(dev->descriptor.idVendor) && + id->dev.idProduct == le16_to_cpu(dev->descriptor.idProduct)) { + ret = id->index; + break; + } + } + return ret; +} + +static int call_battery_notify(struct usb_device *dev, bool on) +{ + struct usb_device *hdev; + struct usb_device *udev; + struct usb_hub *hub; + struct otg_notify *o_notify = get_otg_notify(); + int index = 0; + int count = 0; + int port; + + index = is_notify_hub(dev); + if (!index) + goto skip; + if (check_essential_device(dev, index)) + goto skip; + + hdev = dev->parent; + hub = usb_hub_to_struct_hub(hdev); + if (!hub) + goto skip; + + for (port = 1; port <= hdev->maxchild; port++) { + udev = hub->ports[port-1]->child; + if (udev) { + if (!check_essential_device(udev, index)) { + if (!on && (udev == dev)) + continue; + else + count++; + } + } + } + + pr_info("%s : VID : 0x%x, PID : 0x%x, on=%d, count=%d\n", __func__, + dev->descriptor.idVendor, dev->descriptor.idProduct, + on, count); + if (on) { + if (count == 1) { + if (index == SMARTDOCK_INDEX) + send_otg_notify(o_notify, + NOTIFY_EVENT_SMTD_EXT_CURRENT, 1); + else if (index == MMDOCK_INDEX) + send_otg_notify(o_notify, + NOTIFY_EVENT_MMD_EXT_CURRENT, 1); + } + } else { + if (!count) { + if (index == SMARTDOCK_INDEX) + send_otg_notify(o_notify, + NOTIFY_EVENT_SMTD_EXT_CURRENT, 0); + else if (index == MMDOCK_INDEX) + send_otg_notify(o_notify, + NOTIFY_EVENT_MMD_EXT_CURRENT, 0); + } + } +skip: + return 0; +} + +static int call_device_notify(struct usb_device *dev) +{ + struct otg_notify *o_notify = get_otg_notify(); + + if (dev->bus->root_hub != dev) { + pr_info("%s device\n", __func__); + send_otg_notify(o_notify, NOTIFY_EVENT_DEVICE_CONNECT, 1); + + if (check_gamepad_device(dev)) + send_otg_notify(o_notify, + NOTIFY_EVENT_GAMEPAD_CONNECT, 1); + else if (check_lanhub_device(dev)) + send_otg_notify(o_notify, + NOTIFY_EVENT_LANHUB_CONNECT, 1); + else + ; + } else + pr_info("%s root hub\n", __func__); + + return 0; +} + +static int update_hub_autosuspend_timer(struct usb_device *dev) +{ + struct usb_device *hdev; + int time = 0; + + if (!dev) + goto skip; + + hdev = dev->parent; + + if (hdev == NULL || dev->bus->root_hub != hdev) + goto skip; + + /* hdev is root hub */ + time = get_autosuspend_time(dev); + if (time == hdev->dev.power.autosuspend_delay) + goto skip; + + pm_runtime_set_autosuspend_delay(&hdev->dev, time*1000); + pr_info("set autosuspend delay time=%d sec\n", time); +skip: + return 0; +} + +static void check_device_speed(struct usb_device *dev, bool on) +{ + struct otg_notify *o_notify = get_otg_notify(); + struct usb_device *hdev; + int speed = USB_SPEED_UNKNOWN; + + if (!o_notify) { + pr_err("%s otg_notify is null\n", __func__); + return; + } + + hdev = dev->parent; + if (!hdev) + return; + if (on) + speed = dev->speed; + + o_notify->speed = speed; + + switch (speed) { + case USB_SPEED_SUPER: + pr_info("%s : %s superspeed device\n", + __func__, (on ? "attached" : "detached")); + break; + case USB_SPEED_HIGH: + pr_info("%s : %s highspeed device\n", + __func__, (on ? "attached" : "detached")); + break; + case USB_SPEED_FULL: + pr_info("%s : %s fullspeed device\n", + __func__, (on ? "attached" : "detached")); + break; + case USB_SPEED_LOW: + pr_info("%s : %s lowspeed device\n", + __func__, (on ? "attached" : "detached")); + break; + } +} + +#if defined(CONFIG_USB_HW_PARAM) +static int set_hw_param(struct usb_device *dev) +{ + struct otg_notify *o_notify = get_otg_notify(); + int ret = 0; + int bInterfaceClass = 0, speed = 0; + + if (o_notify == NULL || dev->config->interface[0] == NULL) { + ret = -EFAULT; + goto err; + } + + if (dev->bus->root_hub != dev) { + bInterfaceClass = dev->config->interface[0]-> + cur_altsetting->desc.bInterfaceClass; + speed = dev->speed; + + pr_info("%s USB device connected - Class : 0x%x, speed : 0x%x\n", + __func__, bInterfaceClass, speed); + + if (bInterfaceClass == USB_CLASS_AUDIO) + inc_hw_param(o_notify, USB_HOST_CLASS_AUDIO_COUNT); + else if (bInterfaceClass == USB_CLASS_COMM) + inc_hw_param(o_notify, USB_HOST_CLASS_COMM_COUNT); + else if (bInterfaceClass == USB_CLASS_HID) + inc_hw_param(o_notify, USB_HOST_CLASS_HID_COUNT); + else if (bInterfaceClass == USB_CLASS_PHYSICAL) + inc_hw_param(o_notify, USB_HOST_CLASS_PHYSICAL_COUNT); + else if (bInterfaceClass == USB_CLASS_STILL_IMAGE) + inc_hw_param(o_notify, USB_HOST_CLASS_IMAGE_COUNT); + else if (bInterfaceClass == USB_CLASS_PRINTER) + inc_hw_param(o_notify, USB_HOST_CLASS_PRINTER_COUNT); + else if (bInterfaceClass == USB_CLASS_MASS_STORAGE) + inc_hw_param(o_notify, USB_HOST_CLASS_STORAGE_COUNT); + else if (bInterfaceClass == USB_CLASS_HUB) + inc_hw_param(o_notify, USB_HOST_CLASS_HUB_COUNT); + else if (bInterfaceClass == USB_CLASS_CDC_DATA) + inc_hw_param(o_notify, USB_HOST_CLASS_CDC_COUNT); + else if (bInterfaceClass == USB_CLASS_CSCID) + inc_hw_param(o_notify, USB_HOST_CLASS_CSCID_COUNT); + else if (bInterfaceClass == USB_CLASS_CONTENT_SEC) + inc_hw_param(o_notify, USB_HOST_CLASS_CONTENT_COUNT); + else if (bInterfaceClass == USB_CLASS_VIDEO) + inc_hw_param(o_notify, USB_HOST_CLASS_VIDEO_COUNT); + else if (bInterfaceClass == USB_CLASS_WIRELESS_CONTROLLER) + inc_hw_param(o_notify, USB_HOST_CLASS_WIRELESS_COUNT); + else if (bInterfaceClass == USB_CLASS_MISC) + inc_hw_param(o_notify, USB_HOST_CLASS_MISC_COUNT); + else if (bInterfaceClass == USB_CLASS_APP_SPEC) + inc_hw_param(o_notify, USB_HOST_CLASS_APP_COUNT); + else if (bInterfaceClass == USB_CLASS_VENDOR_SPEC) + inc_hw_param(o_notify, USB_HOST_CLASS_VENDOR_COUNT); + + if (speed == USB_SPEED_SUPER) + inc_hw_param(o_notify, USB_HOST_SUPER_SPEED_COUNT); + else if (speed == USB_SPEED_HIGH) + inc_hw_param(o_notify, USB_HOST_HIGH_SPEED_COUNT); + else if (speed == USB_SPEED_FULL) + inc_hw_param(o_notify, USB_HOST_FULL_SPEED_COUNT); + else if (speed == USB_SPEED_LOW) + inc_hw_param(o_notify, USB_HOST_LOW_SPEED_COUNT); + } +err: + return ret; +} +#endif +static int dev_notify(struct notifier_block *self, + unsigned long action, void *dev) +{ + switch (action) { + case USB_DEVICE_ADD: + call_device_notify(dev); + call_battery_notify(dev, 1); + check_device_speed(dev, 1); + update_hub_autosuspend_timer(dev); +#if defined(CONFIG_USB_HW_PARAM) + set_hw_param(dev); +#endif + break; + case USB_DEVICE_REMOVE: + call_battery_notify(dev, 0); + check_device_speed(dev, 0); + break; + } + return NOTIFY_OK; +} + +static struct notifier_block dev_nb = { + .notifier_call = dev_notify, +}; + +void register_usbdev_notify(void) +{ + usb_register_notify(&dev_nb); +} +EXPORT_SYMBOL(register_usbdev_notify); + +void unregister_usbdev_notify(void) +{ + usb_unregister_notify(&dev_nb); +} +EXPORT_SYMBOL(unregister_usbdev_notify); + diff --git a/drivers/usb/notify/dock_notify.h b/drivers/usb/notify/dock_notify.h new file mode 100644 index 000000000000..b9e90b5a93aa --- /dev/null +++ b/drivers/usb/notify/dock_notify.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2015-2017 Samsung Electronics Co. Ltd. + * + * 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. + */ + + /* usb notify layer v3.1 */ + +#ifndef __LINUX_DOCK_NOTIFY_H__ +#define __LINUX_DOCK_NOTIFY_H__ + +extern void register_usbdev_notify(void); +extern void unregister_usbdev_notify(void); +#endif + diff --git a/drivers/usb/notify/external_notify.c b/drivers/usb/notify/external_notify.c new file mode 100644 index 000000000000..917c97c62292 --- /dev/null +++ b/drivers/usb/notify/external_notify.c @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2016-2017 Samsung Electronics Co. Ltd. + * + * 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. + */ + + /* usb notify layer v3.1 */ + +#define pr_fmt(fmt) "usb_notify: " fmt + +#include +#include +#include +#include +#include + +struct external_notify_struct { + struct blocking_notifier_head notifier_call_chain; + int call_chain_init; +}; + +#define SET_EXTERNAL_NOTIFY_BLOCK(nb, fn, dev) do { \ + (nb)->notifier_call = (fn); \ + (nb)->priority = (dev); \ + } while (0) + +#define DESTROY_EXTERNAL_NOTIFY_BLOCK(nb) \ + SET_EXTERNAL_NOTIFY_BLOCK(nb, NULL, -1) + +static struct external_notify_struct external_notifier; + +static const char *cmd_string(unsigned long cmd) +{ + switch (cmd) { + case EXTERNAL_NOTIFY_3S_NODEVICE: + return "3s_no_device"; + case EXTERNAL_NOTIFY_DEVICE_CONNECT: + return "device_connect"; + case EXTERNAL_NOTIFY_HOSTBLOCK_PRE: + return "host_block_pre"; + case EXTERNAL_NOTIFY_HOSTBLOCK_POST: + return "host_block_post"; + case EXTERNAL_NOTIFY_MDMBLOCK_PRE: + return "mdm_block_pre"; + case EXTERNAL_NOTIFY_MDMBLOCK_POST: + return "mdm_block_post"; + case EXTERNAL_NOTIFY_POWERROLE: + return "power_role_notify"; + default: + return "undefined"; + } +} + +static const char *listener_string(int listener) +{ + switch (listener) { + case EXTERNAL_NOTIFY_DEV_MUIC: + return "muic"; + case EXTERNAL_NOTIFY_DEV_CHARGER: + return "charger"; + case EXTERNAL_NOTIFY_DEV_PDIC: + return "pdic"; + default: + return "undefined"; + } +} + +static int create_external_notify(void) +{ + if (!external_notifier.call_chain_init) { + pr_info("%s\n", __func__); + BLOCKING_INIT_NOTIFIER_HEAD + (&(external_notifier.notifier_call_chain)); + external_notifier.call_chain_init = 1; + } + return 0; +} + +int usb_external_notify_register(struct notifier_block *nb, + notifier_fn_t notifier, int listener) +{ + int ret = 0; + + pr_info("%s: listener=(%s)%d register\n", __func__, + listener_string(listener), listener); + + create_external_notify(); + + SET_EXTERNAL_NOTIFY_BLOCK(nb, notifier, listener); + ret = blocking_notifier_chain_register + (&(external_notifier.notifier_call_chain), nb); + if (ret < 0) + pr_err("%s: blocking_notifier_chain_register error(%d)\n", + __func__, ret); + + return ret; +} + +int usb_external_notify_unregister(struct notifier_block *nb) +{ + int ret = 0; + + pr_info("%s: listener=(%s)%d unregister\n", __func__, + listener_string(nb->priority), + nb->priority); + + ret = blocking_notifier_chain_unregister + (&(external_notifier.notifier_call_chain), nb); + if (ret < 0) + pr_err("%s: blocking_notifier_chain_unregister error(%d)\n", + __func__, ret); + DESTROY_EXTERNAL_NOTIFY_BLOCK(nb); + + return ret; +} + +int send_external_notify(unsigned long cmd, int data) +{ + int ret = 0; + + pr_info("%s: cmd=%s(%lu), data=%d\n", __func__, cmd_string(cmd), + cmd, data); + + create_external_notify(); + + ret = blocking_notifier_call_chain + (&(external_notifier.notifier_call_chain), + cmd, (void *)&(data)); + + switch (ret) { + case NOTIFY_STOP_MASK: + case NOTIFY_BAD: + pr_err("%s: notify error occur(0x%x)\n", __func__, ret); + break; + case NOTIFY_DONE: + case NOTIFY_OK: + pr_info("%s: notify done(0x%x)\n", __func__, ret); + break; + default: + pr_info("%s: notify status unknown(0x%x)\n", __func__, ret); + break; + } + + return ret; +} + +static int __init external_notifier_init(void) +{ + int ret = 0; + + pr_info("%s\n", __func__); + create_external_notify(); + + return ret; +} +module_init(external_notifier_init); diff --git a/drivers/usb/notify/host_notify_class.c b/drivers/usb/notify/host_notify_class.c new file mode 100644 index 000000000000..2db648caafc6 --- /dev/null +++ b/drivers/usb/notify/host_notify_class.c @@ -0,0 +1,306 @@ +/* + * drivers/usb/notify/host_notify_class.c + * + * Copyright (C) 2011-2017 Samsung, Inc. + * Author: Dongrak Shin + * +*/ + + /* usb notify layer v3.1 */ + +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(CONFIG_USB_HW_PARAM) +#include +#endif + +struct notify_data { + struct class *host_notify_class; + atomic_t device_count; +}; + +static struct notify_data host_notify; + +static ssize_t mode_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + struct host_notify_dev *ndev = (struct host_notify_dev *) + dev_get_drvdata(dev); + char *mode; + + switch (ndev->mode) { + case NOTIFY_HOST_MODE: + mode = "HOST"; + break; + case NOTIFY_PERIPHERAL_MODE: + mode = "PERIPHERAL"; + break; + case NOTIFY_TEST_MODE: + mode = "TEST"; + break; + case NOTIFY_NONE_MODE: + default: + mode = "NONE"; + break; + } + + return snprintf(buf, sizeof(mode)+1, "%s\n", mode); +} + +static ssize_t mode_store( + struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + struct host_notify_dev *ndev = (struct host_notify_dev *) + dev_get_drvdata(dev); + + char *mode; + size_t ret = -ENOMEM; + int sret = 0; + + mode = kzalloc(size+1, GFP_KERNEL); + if (!mode) + goto error; + + sret = sscanf(buf, "%s", mode); + if (sret != 1) + goto error1; + + if (ndev->set_mode) { + pr_info("host_notify: set mode %s\n", mode); + if (!strncmp(mode, "HOST", 4)) + ndev->set_mode(NOTIFY_SET_ON); + else if (!strncmp(mode, "NONE", 4)) + ndev->set_mode(NOTIFY_SET_OFF); + } + ret = size; +error1: + kfree(mode); +error: + return ret; +} + +static ssize_t booster_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct host_notify_dev *ndev = (struct host_notify_dev *) + dev_get_drvdata(dev); + char *booster; + + switch (ndev->booster) { + case NOTIFY_POWER_ON: + booster = "ON"; + break; + case NOTIFY_POWER_OFF: + default: + booster = "OFF"; + break; + } + + pr_info("host_notify: read booster %s\n", booster); + return snprintf(buf, sizeof(booster)+1, "%s\n", booster); +} + +static ssize_t booster_store( + struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + struct host_notify_dev *ndev = (struct host_notify_dev *) + dev_get_drvdata(dev); + + char *booster; + size_t ret = -ENOMEM; + int sret = 0; + + booster = kzalloc(size+1, GFP_KERNEL); + if (!booster) + goto error; + + sret = sscanf(buf, "%s", booster); + if (sret != 1) + goto error1; + + if (ndev->set_booster) { + pr_info("host_notify: set booster %s\n", booster); + if (!strncmp(booster, "ON", 2)) { + ndev->set_booster(NOTIFY_SET_ON); + ndev->mode = NOTIFY_TEST_MODE; + } else if (!strncmp(booster, "OFF", 3)) { + ndev->set_booster(NOTIFY_SET_OFF); + ndev->mode = NOTIFY_NONE_MODE; + } + } + ret = size; +error1: + kfree(booster); +error: + return ret; +} + +static DEVICE_ATTR(mode, 0664, mode_show, mode_store); +static DEVICE_ATTR(booster, 0664, booster_show, booster_store); + +static struct attribute *host_notify_attrs[] = { + &dev_attr_mode.attr, + &dev_attr_booster.attr, + NULL, +}; + +static struct attribute_group host_notify_attr_grp = { + .attrs = host_notify_attrs, +}; + +char *host_state_string(int type) +{ + switch (type) { + case NOTIFY_HOST_NONE: return "none"; + case NOTIFY_HOST_ADD: return "add"; + case NOTIFY_HOST_REMOVE: return "remove"; + case NOTIFY_HOST_OVERCURRENT: return "overcurrent"; + case NOTIFY_HOST_LOWBATT: return "lowbatt"; + case NOTIFY_HOST_BLOCK: return "block"; + case NOTIFY_HOST_UNKNOWN: + default: return "unknown"; + } +} + +int host_state_notify(struct host_notify_dev *ndev, int state) +{ + pr_info("host_notify: ndev name=%s: (%s --> %s)\n", + ndev->name, + host_state_string(ndev->state), + host_state_string(state)); + + if (ndev->state != state) { + ndev->state = state; + if (state != NOTIFY_HOST_NONE) + kobject_uevent(&ndev->dev->kobj, KOBJ_CHANGE); +#if defined(CONFIG_USB_HW_PARAM) + if (state == NOTIFY_HOST_ADD) + inc_hw_param_host(ndev, USB_CCIC_OTG_USE_COUNT); + else if (state == NOTIFY_HOST_OVERCURRENT) + inc_hw_param_host(ndev, USB_CCIC_OVC_COUNT); + else + ; +#endif + return 1; + } + return 0; +} +EXPORT_SYMBOL_GPL(host_state_notify); + +static int +host_notify_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + struct host_notify_dev *ndev = (struct host_notify_dev *) + dev_get_drvdata(dev); + char *state; + + if (!ndev) { + /* this happens when the device is first created */ + return 0; + } + switch (ndev->state) { + case NOTIFY_HOST_ADD: + state = "ADD"; + break; + case NOTIFY_HOST_REMOVE: + state = "REMOVE"; + break; + case NOTIFY_HOST_OVERCURRENT: + state = "OVERCURRENT"; + break; + case NOTIFY_HOST_LOWBATT: + state = "LOWBATT"; + break; + case NOTIFY_HOST_BLOCK: + state = "BLOCK"; + break; + case NOTIFY_HOST_UNKNOWN: + state = "UNKNOWN"; + break; + case NOTIFY_HOST_NONE: + default: + return 0; + } + if (add_uevent_var(env, "DEVNAME=%s", ndev->dev->kobj.name)) + return -ENOMEM; + if (add_uevent_var(env, "STATE=%s", state)) + return -ENOMEM; + return 0; +} + +static int create_notify_class(void) +{ + if (!host_notify.host_notify_class) { + host_notify.host_notify_class + = class_create(THIS_MODULE, "host_notify"); + if (IS_ERR(host_notify.host_notify_class)) + return PTR_ERR(host_notify.host_notify_class); + atomic_set(&host_notify.device_count, 0); + host_notify.host_notify_class->dev_uevent = host_notify_uevent; + } + + return 0; +} + +int host_notify_dev_register(struct host_notify_dev *ndev) +{ + int ret; + + if (!host_notify.host_notify_class) { + ret = create_notify_class(); + if (ret < 0) + return ret; + } + + ndev->index = atomic_inc_return(&host_notify.device_count); + ndev->dev = device_create(host_notify.host_notify_class, NULL, + MKDEV(0, ndev->index), NULL, ndev->name); + if (IS_ERR(ndev->dev)) + return PTR_ERR(ndev->dev); + + ret = sysfs_create_group(&ndev->dev->kobj, &host_notify_attr_grp); + if (ret < 0) { + device_destroy(host_notify.host_notify_class, + MKDEV(0, ndev->index)); + return ret; + } + + dev_set_drvdata(ndev->dev, ndev); + ndev->state = 0; + return 0; +} +EXPORT_SYMBOL_GPL(host_notify_dev_register); + +void host_notify_dev_unregister(struct host_notify_dev *ndev) +{ + ndev->state = NOTIFY_HOST_NONE; + sysfs_remove_group(&ndev->dev->kobj, &host_notify_attr_grp); + device_destroy(host_notify.host_notify_class, MKDEV(0, ndev->index)); + dev_set_drvdata(ndev->dev, NULL); +} +EXPORT_SYMBOL_GPL(host_notify_dev_unregister); + +static int __init notify_class_init(void) +{ + return create_notify_class(); +} + +static void __exit notify_class_exit(void) +{ + class_destroy(host_notify.host_notify_class); +} + +module_init(notify_class_init); +module_exit(notify_class_exit); + +MODULE_AUTHOR("Dongrak Shin "); +MODULE_DESCRIPTION("Usb host notify driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/notify/usb_notifier.c b/drivers/usb/notify/usb_notifier.c new file mode 100644 index 000000000000..c54c4408daf1 --- /dev/null +++ b/drivers/usb/notify/usb_notifier.c @@ -0,0 +1,1006 @@ +/* + * Copyright (C) 2011 Samsung Electronics Co. Ltd. + * Inchul Im + * + * 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. + */ +#define pr_fmt(fmt) "usb_notifier: " fmt + +#include +#include +#include +#ifdef CONFIG_OF +#include +#include +#endif +#if defined(CONFIG_IFCONN_NOTIFIER) +#include +#endif +#if defined(CONFIG_CCIC_NOTIFIER) +#include +#endif +#if defined(CONFIG_MUIC_NOTIFIER) || defined(CONFIG_IFCONN_NOTIFIER) +#include +#endif +#if defined(CONFIG_MUIC_NOTIFIER) +#include +#endif +#if defined(CONFIG_VBUS_NOTIFIER) +#include +#endif +#if defined(CONFIG_USB_TYPEC_MANAGER_NOTIFIER) +#include +#endif +#if defined(CONFIG_BATTERY_SAMSUNG_V2) +#include "../../battery_v2/include/sec_charging_common.h" +#else +#include +#endif +#include "usb_notifier.h" + +#include +#include + +struct usb_notifier_platform_data { +#if defined(CONFIG_CCIC_NOTIFIER) || defined(CONFIG_IFCONN_NOTIFIER) + struct notifier_block ccic_usb_nb; + int is_host; +#endif +#if defined(CONFIG_MUIC_NOTIFIER) || defined(CONFIG_IFCONN_NOTIFIER) + struct notifier_block muic_usb_nb; +#endif +#if defined(CONFIG_VBUS_NOTIFIER) || defined(CONFIG_IFCONN_NOTIFIER) + struct notifier_block vbus_nb; +#endif + int gpio_redriver_en; + int can_disable_usb; + +#if defined(CONFIG_CCIC_NOTIFIER) + struct delayed_work usb_ldo_work; + unsigned int usb_ldocontrol; + unsigned int usb_ldo_onoff; + bool usb_ldo_off_working; + const char *hs_vdd; + const char *ss_vdd; + const char *dp_vdd; +#endif +}; + +#ifdef CONFIG_OF +static void of_get_usb_redriver_dt(struct device_node *np, + struct usb_notifier_platform_data *pdata) +{ + int gpio = 0; + + gpio = of_get_named_gpio(np, "gpios_redriver_en", 0); + if (!gpio_is_valid(gpio)) { + pdata->gpio_redriver_en = -1; + pr_err("%s: usb30_redriver_en: Invalied gpio pins\n", __func__); + } else + pdata->gpio_redriver_en = gpio; + + pr_info("%s, gpios_redriver_en %d\n", __func__, gpio); + + pdata->can_disable_usb = + !(of_property_read_bool(np, "samsung,unsupport-disable-usb")); + pr_info("%s, can_disable_usb %d\n", __func__, pdata->can_disable_usb); + +#if defined(CONFIG_CCIC_NOTIFIER) + if (of_property_read_string(np, "hs-regulator", &pdata->hs_vdd) < 0) { + pr_err("%s - get hs_vdd error\n", __func__); + pdata->hs_vdd = NULL; + } + if (of_property_read_string(np, "ss-regulator", &pdata->ss_vdd) < 0) { + pr_err("%s - get ss_vdd error\n", __func__); + pdata->ss_vdd = NULL; + } + if (of_property_read_string(np, "dp-regulator", &pdata->dp_vdd) < 0) { + pr_err("%s - get dp_vdd error\n", __func__); + pdata->dp_vdd = NULL; + } + + if (of_property_read_u32(np, "usb-ldocontrol", &pdata->usb_ldocontrol)) + pdata->usb_ldocontrol = 0; + + pr_info("%s, usb_ldocontrol %d\n", __func__, pdata->usb_ldocontrol); +#endif +} + +static int of_usb_notifier_dt(struct device *dev, + struct usb_notifier_platform_data *pdata) +{ + struct device_node *np = dev->of_node; + + if (!np) + return -EINVAL; + + of_get_usb_redriver_dt(np, pdata); + return 0; +} +#endif + +static struct device_node *exynos_udc_parse_dt(void) +{ + struct platform_device *pdev = NULL; + struct device *dev = NULL; + struct device_node *np = NULL; + + /** + * For previous chips such as Exynos7420 and Exynos7890 + */ + np = of_find_compatible_node(NULL, NULL, "samsung,exynos5-dwusb3"); + if (np) + goto find; + + np = of_find_compatible_node(NULL, NULL, "samsung,usb-notifier"); + if (!np) { + pr_err("%s: failed to get the usb-notifier device node\n", + __func__); + goto err; + } + + pdev = of_find_device_by_node(np); + if (!pdev) { + pr_err("%s: failed to get platform_device\n", __func__); + goto err; + } + + dev = &pdev->dev; + np = of_parse_phandle(dev->of_node, "udc", 0); + if (!np) { + dev_info(dev, "udc device is not available\n"); + goto err; + } +find: + return np; +err: + return NULL; +} + +#if defined(CONFIG_CCIC_NOTIFIER) +static void usb_hs_regulator_onoff(struct usb_notifier_platform_data *pdata, + unsigned int onoff) +{ + struct regulator *avdd33_usb; + int ret; + + if (!pdata->hs_vdd) { + pr_err("%s hs_vdd is null\n", __func__); + return; + } + + avdd33_usb = regulator_get(NULL, pdata->hs_vdd); + if (IS_ERR(avdd33_usb) || avdd33_usb == NULL) { + pr_err("%s - avdd33_usb regulator_get fail\n", __func__); + return; + } + + if (onoff) { + ret = regulator_enable(avdd33_usb); + if (ret) { + pr_err("%s - enable avdd33_usb ldo enable failed, ret=%d\n", + __func__, ret); + } + } else { + ret = regulator_disable(avdd33_usb); + if (ret) { + pr_err("%s - enable avdd33_usb ldo disable failed, ret=%d\n", + __func__, ret); + } + } + + regulator_put(avdd33_usb); +} + +static void usb_ss_regulator_onoff(struct usb_notifier_platform_data *pdata, + bool onoff) +{ + struct regulator *vdd085_usb; + int ret; + + if (!pdata->ss_vdd) { + pr_err("%s ss_vdd is null\n", __func__); + return; + } + + vdd085_usb = regulator_get(NULL, pdata->ss_vdd); + if (IS_ERR(vdd085_usb) || vdd085_usb == NULL) { + pr_err("%s - vdd085_usb regulator_get fail\n", __func__); + return; + } + + if (onoff) { + ret = regulator_enable(vdd085_usb); + if (ret) { + pr_err("%s - enable vdd085_usb ldo enable failed, ret=%d\n", + __func__, ret); + } + } else { + ret = regulator_disable(vdd085_usb); + if (ret) { + pr_err("%s - enable vdd085_usb ldo disable failed, ret=%d\n", + __func__, ret); + } + } + + regulator_put(vdd085_usb); +} + +static void usb_dp_regulator_onoff(struct usb_notifier_platform_data *pdata, + unsigned int onoff) +{ + struct regulator *vdd3p3_dp; + int ret; + + if (!pdata->dp_vdd) { + pr_err("%s dp_vdd is null\n", __func__); + return; + } + + vdd3p3_dp = regulator_get(NULL, pdata->dp_vdd); + if (IS_ERR(vdd3p3_dp) || vdd3p3_dp == NULL) { + pr_err("%s - dp_3p3 regulator_get fail\n", __func__); + return; + } + + if (onoff) { + ret = regulator_enable(vdd3p3_dp); + if (ret) { + pr_err("%s - enable dp_3p3 ldo enable failed, ret=%d\n", + __func__, ret); + } + } else { + ret = regulator_disable(vdd3p3_dp); + if (ret) { + pr_err("%s - enable dp_3p3 ldo disable failed, ret=%d\n", + __func__, ret); + } + } + + regulator_put(vdd3p3_dp); +} + +static void usb_regulator_onoff(void *data, unsigned int onoff) +{ + struct usb_notifier_platform_data *pdata = + (struct usb_notifier_platform_data *)(data); + pr_info("usb: %s - Turn %s (ldocontrol=%d, usb_ldo_off_working=%d)\n", __func__, + onoff ? "on":"off", pdata->usb_ldocontrol, pdata->usb_ldo_off_working); + + if (pdata->usb_ldocontrol) { + if (onoff) { + if (!pdata->usb_ldo_onoff) { + if (pdata->usb_ldo_off_working) { + cancel_delayed_work_sync(&pdata->usb_ldo_work); + pdata->usb_ldo_off_working = false; + } else { + usb_ss_regulator_onoff(pdata, onoff); + usb_dp_regulator_onoff(pdata, onoff); + usb_hs_regulator_onoff(pdata, onoff); + } + } else { + pr_err("%s already on\n", __func__); + } + } else { + if (pdata->usb_ldo_onoff) { + pdata->usb_ldo_off_working = true; + schedule_delayed_work(&pdata->usb_ldo_work, msecs_to_jiffies(2000)); + } else { + pr_err("%s already off\n", __func__); + } + } + pdata->usb_ldo_onoff = onoff; + } else { + pr_err("%s: can't control\n", __func__); + } +} + +static void usb_ldo_off_control(struct work_struct *work) +{ + struct usb_notifier_platform_data *pdata = container_of(work, + struct usb_notifier_platform_data, + usb_ldo_work.work); + + pr_info("usb: %s - working=%d\n", __func__, pdata->usb_ldo_off_working); + if (pdata->usb_ldo_off_working) { + pdata->usb_ldo_off_working = false; + usb_hs_regulator_onoff(pdata, false); + usb_dp_regulator_onoff(pdata, false); + usb_ss_regulator_onoff(pdata, false); + } +} +#endif + +static void check_usb_vbus_state(int state) +{ + struct device_node *np = NULL; + struct platform_device *pdev = NULL; + + pr_info("%s vbus state = %d\n", __func__, state); + + np = exynos_udc_parse_dt(); + if (np) { + pdev = of_find_device_by_node(np); + of_node_put(np); + if (pdev) { + pr_info("%s: get the %s platform_device\n", + __func__, pdev->name); + dwc3_exynos_vbus_event(&pdev->dev, state); + goto end; + } + } + + pr_err("%s: failed to get the platform_device\n", __func__); +end: + return; +} + +static void check_usb_id_state(int state) +{ + struct device_node *np = NULL; + struct platform_device *pdev = NULL; + + pr_info("%s id state = %d\n", __func__, state); + + np = exynos_udc_parse_dt(); + if (np) { + pdev = of_find_device_by_node(np); + of_node_put(np); + if (pdev) { + pr_info("%s: get the %s platform_device\n", + __func__, pdev->name); + dwc3_exynos_id_event(&pdev->dev, state); + goto end; + } + } + pr_err("%s: failed to get the platform_device\n", __func__); +end: + return; +} + +#if defined(CONFIG_IFCONN_NOTIFIER) +static int ccic_usb_handle_notification(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct ifconn_notifier_template usb_status = *(struct ifconn_notifier_template *)data; + struct otg_notify *o_notify = get_otg_notify(); + struct usb_notifier_platform_data *pdata = + container_of(nb, struct usb_notifier_platform_data, ccic_usb_nb); + + if(usb_status.dest != IFCONN_NOTIFY_USB) { + return 0; + } + + switch (usb_status.event){ + case IFCONN_NOTIFY_EVENT_USB_ATTACH_DFP: + pr_info("%s: Turn On Host(DFP)\n", __func__); + send_otg_notify(o_notify, NOTIFY_EVENT_HOST, 1); + pdata->is_host = 1; + break; + case IFCONN_NOTIFY_EVENT_USB_ATTACH_UFP: + pr_info("%s: Turn On Device(UFP)\n", __func__); + send_otg_notify(o_notify, NOTIFY_EVENT_VBUS, 1); + if(is_blocked(o_notify, NOTIFY_BLOCK_TYPE_CLIENT)) + return -EPERM; + break; + case IFCONN_NOTIFY_EVENT_DETACH: + if(pdata->is_host) { + pr_info("%s: Turn Off Host(DFP)\n", __func__); + send_otg_notify(o_notify, NOTIFY_EVENT_HOST, 0); + pdata->is_host = 0; + } else { + pr_info("%s: Turn Off Device(UFP)\n", __func__); + send_otg_notify(o_notify, NOTIFY_EVENT_VBUS, 0); + } + break; + default: + pr_info("%s: unsupported DRP type : %d.\n", __func__, usb_status.drp); + break; + } + return 0; +} + +static int muic_usb_handle_notification(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct ifconn_notifier_template *p_noti = (struct ifconn_notifier_template *)data; + muic_attached_dev_t attached_dev = (muic_attached_dev_t)p_noti->event; + struct otg_notify *o_notify = get_otg_notify(); + + pr_info("%s action=%lu, attached_dev=%d\n", + __func__, action, attached_dev); + + switch (attached_dev) { + case ATTACHED_DEV_USB_MUIC: + case ATTACHED_DEV_CDP_MUIC: + case ATTACHED_DEV_UNOFFICIAL_ID_USB_MUIC: + case ATTACHED_DEV_UNOFFICIAL_ID_CDP_MUIC: + case ATTACHED_DEV_JIG_USB_OFF_MUIC: + case ATTACHED_DEV_JIG_USB_ON_MUIC: + if (action == IFCONN_NOTIFY_ID_DETACH) + send_otg_notify(o_notify, NOTIFY_EVENT_VBUS, 0); + else if (action == IFCONN_NOTIFY_ID_ATTACH) + send_otg_notify(o_notify, NOTIFY_EVENT_VBUS, 1); + else + pr_err("%s - ACTION Error!\n", __func__); + break; + case ATTACHED_DEV_OTG_MUIC: + if (action == IFCONN_NOTIFY_ID_DETACH) + send_otg_notify(o_notify, NOTIFY_EVENT_HOST, 0); + else if (action == IFCONN_NOTIFY_ID_ATTACH) + send_otg_notify(o_notify, NOTIFY_EVENT_HOST, 1); + else + pr_err("%s - ACTION Error!\n", __func__); + break; + case ATTACHED_DEV_HMT_MUIC: + if (action == IFCONN_NOTIFY_ID_DETACH) + send_otg_notify(o_notify, NOTIFY_EVENT_HMT, 0); + else if (action == IFCONN_NOTIFY_ID_ATTACH) + send_otg_notify(o_notify, NOTIFY_EVENT_HMT, 1); + else + pr_err("%s - ACTION Error!\n", __func__); + break; + case ATTACHED_DEV_JIG_UART_OFF_VB_OTG_MUIC: + if (action == IFCONN_NOTIFY_ID_DETACH) + pr_info("%s - USB_HOST_TEST_DETACHED\n", __func__); + else if (action == IFCONN_NOTIFY_ID_ATTACH) + pr_info("%s - USB_HOST_TEST_ATTACHED\n", __func__); + else + pr_err("%s - ACTION Error!\n", __func__); + break; + case ATTACHED_DEV_SMARTDOCK_TA_MUIC: + if (action == IFCONN_NOTIFY_ID_DETACH) + send_otg_notify(o_notify, NOTIFY_EVENT_SMARTDOCK_TA, 0); + else if (action == IFCONN_NOTIFY_ID_ATTACH) + send_otg_notify(o_notify, NOTIFY_EVENT_SMARTDOCK_TA, 1); + else + pr_err("%s - ACTION Error!\n", __func__); + break; + case ATTACHED_DEV_SMARTDOCK_USB_MUIC: + if (action == IFCONN_NOTIFY_ID_DETACH) + send_otg_notify + (o_notify, NOTIFY_EVENT_SMARTDOCK_USB, 0); + else if (action == IFCONN_NOTIFY_ID_ATTACH) + send_otg_notify + (o_notify, NOTIFY_EVENT_SMARTDOCK_USB, 1); + else + pr_err("%s - ACTION Error!\n", __func__); + break; + case ATTACHED_DEV_AUDIODOCK_MUIC: + if (action == IFCONN_NOTIFY_ID_DETACH) + send_otg_notify(o_notify, NOTIFY_EVENT_AUDIODOCK, 0); + else if (action == IFCONN_NOTIFY_ID_ATTACH) + send_otg_notify(o_notify, NOTIFY_EVENT_AUDIODOCK, 1); + else + pr_err("%s - ACTION Error!\n", __func__); + break; + case ATTACHED_DEV_UNIVERSAL_MMDOCK_MUIC: + if (action == IFCONN_NOTIFY_ID_DETACH) + send_otg_notify(o_notify, NOTIFY_EVENT_MMDOCK, 0); + else if (action == IFCONN_NOTIFY_ID_ATTACH) + send_otg_notify(o_notify, NOTIFY_EVENT_MMDOCK, 1); + else + pr_err("%s - ACTION Error!\n", __func__); + break; + case ATTACHED_DEV_USB_LANHUB_MUIC: + if (action == IFCONN_NOTIFY_ID_DETACH) + send_otg_notify(o_notify, NOTIFY_EVENT_LANHUB, 0); + else if (action == IFCONN_NOTIFY_ID_ATTACH) + send_otg_notify(o_notify, NOTIFY_EVENT_LANHUB, 1); + else + pr_err("%s - ACTION Error!\n", __func__); + break; + case ATTACHED_DEV_GAMEPAD_MUIC: + if (action == IFCONN_NOTIFY_ID_DETACH) { + send_otg_notify(o_notify, NOTIFY_EVENT_GAMEPAD, 0); + } else if (action == IFCONN_NOTIFY_ID_ATTACH) { + send_otg_notify(o_notify, NOTIFY_EVENT_GAMEPAD, 1); + } else + pr_err("%s - ACTION Error!\n", __func__); + break; + default: + break; + } + + return 0; +} + +static int vbus_handle_notification(struct notifier_block *nb, + unsigned long cmd, void *data) +{ + struct ifconn_notifier_template *p_noti = (struct ifconn_notifier_template *)data; + ifconn_notifier_vbus_t vbus_type = p_noti->vbus_type; + struct otg_notify *o_notify; + + o_notify = get_otg_notify(); + + pr_info("%s cmd=%lu, vbus_type=%s\n", + __func__, cmd, vbus_type == IFCONN_NOTIFY_VBUS_HIGH ? "HIGH" : "LOW"); + if (vbus_type == IFCONN_NOTIFY_VBUS_HIGH) { + send_otg_notify(o_notify, NOTIFY_EVENT_VBUSPOWER, 1); + } else if (vbus_type == IFCONN_NOTIFY_VBUS_LOW) { + send_otg_notify(o_notify, NOTIFY_EVENT_VBUSPOWER, 0); + } + return 0; +} + +static int otg_accessory_power(bool enable) +{ +#if !defined(CONFIG_CCIC_S2MM005) + u8 on = (u8)!!enable; + union power_supply_propval val; + + pr_info("otg accessory power = %d\n", on); + + val.intval = enable; + psy_do_property("otg", set, + POWER_SUPPLY_PROP_ONLINE, val); +#endif + return 0; +} +#else +#if defined(CONFIG_CCIC_NOTIFIER) +static int ccic_usb_handle_notification(struct notifier_block *nb, + unsigned long action, void *data) +{ + CC_NOTI_USB_STATUS_TYPEDEF usb_status = *(CC_NOTI_USB_STATUS_TYPEDEF *)data; + struct otg_notify *o_notify = get_otg_notify(); + struct usb_notifier_platform_data *pdata = + container_of(nb, struct usb_notifier_platform_data, ccic_usb_nb); + + if (usb_status.dest != CCIC_NOTIFY_DEV_USB) + return 0; + + switch (usb_status.drp) { + case USB_STATUS_NOTIFY_ATTACH_DFP: + pr_info("%s: Turn On Host(DFP)\n", __func__); + send_otg_notify(o_notify, NOTIFY_EVENT_HOST, 1); + pdata->is_host = 1; + break; + case USB_STATUS_NOTIFY_ATTACH_UFP: + pr_info("%s: Turn On Device(UFP)\n", __func__); + send_otg_notify(o_notify, NOTIFY_EVENT_VBUS, 1); + if (is_blocked(o_notify, NOTIFY_BLOCK_TYPE_CLIENT)) + return -EPERM; + break; + case USB_STATUS_NOTIFY_DETACH: + if (pdata->is_host) { + pr_info("%s: Turn Off Host(DFP)\n", __func__); + send_otg_notify(o_notify, NOTIFY_EVENT_HOST, 0); + pdata->is_host = 0; + } else { + pr_info("%s: Turn Off Device(UFP)\n", __func__); + send_otg_notify(o_notify, NOTIFY_EVENT_VBUS, 0); + } + break; + default: + pr_info("%s: unsupported DRP type : %d.\n", __func__, usb_status.drp); + break; + } + return 0; +} +#endif +#if defined(CONFIG_MUIC_NOTIFIER) +static int muic_usb_handle_notification(struct notifier_block *nb, + unsigned long action, void *data) +{ +#ifdef CONFIG_CCIC_NOTIFIER + CC_NOTI_ATTACH_TYPEDEF *p_noti = (CC_NOTI_ATTACH_TYPEDEF *)data; + muic_attached_dev_t attached_dev = p_noti->cable_type; +#else + muic_attached_dev_t attached_dev = *(muic_attached_dev_t *)data; +#endif + struct otg_notify *o_notify = get_otg_notify(); + + pr_info("%s action=%lu, attached_dev=%d\n", + __func__, action, attached_dev); + + switch (attached_dev) { + case ATTACHED_DEV_USB_MUIC: + case ATTACHED_DEV_CDP_MUIC: + case ATTACHED_DEV_UNOFFICIAL_ID_USB_MUIC: + case ATTACHED_DEV_UNOFFICIAL_ID_CDP_MUIC: + case ATTACHED_DEV_JIG_USB_OFF_MUIC: + case ATTACHED_DEV_JIG_USB_ON_MUIC: + if (action == MUIC_NOTIFY_CMD_DETACH) + send_otg_notify(o_notify, NOTIFY_EVENT_VBUS, 0); + else if (action == MUIC_NOTIFY_CMD_ATTACH) + send_otg_notify(o_notify, NOTIFY_EVENT_VBUS, 1); + else + pr_err("%s - ACTION Error!\n", __func__); + break; + case ATTACHED_DEV_OTG_MUIC: + if (action == MUIC_NOTIFY_CMD_DETACH) + send_otg_notify(o_notify, NOTIFY_EVENT_HOST, 0); + else if (action == MUIC_NOTIFY_CMD_ATTACH) + send_otg_notify(o_notify, NOTIFY_EVENT_HOST, 1); + else + pr_err("%s - ACTION Error!\n", __func__); + break; + case ATTACHED_DEV_HMT_MUIC: + if (action == MUIC_NOTIFY_CMD_DETACH) + send_otg_notify(o_notify, NOTIFY_EVENT_HMT, 0); + else if (action == MUIC_NOTIFY_CMD_ATTACH) + send_otg_notify(o_notify, NOTIFY_EVENT_HMT, 1); + else + pr_err("%s - ACTION Error!\n", __func__); + break; + case ATTACHED_DEV_JIG_UART_OFF_VB_OTG_MUIC: + if (action == MUIC_NOTIFY_CMD_DETACH) + pr_info("%s - USB_HOST_TEST_DETACHED\n", __func__); + else if (action == MUIC_NOTIFY_CMD_ATTACH) + pr_info("%s - USB_HOST_TEST_ATTACHED\n", __func__); + else + pr_err("%s - ACTION Error!\n", __func__); + break; + case ATTACHED_DEV_SMARTDOCK_TA_MUIC: + if (action == MUIC_NOTIFY_CMD_DETACH) + send_otg_notify(o_notify, NOTIFY_EVENT_SMARTDOCK_TA, 0); + else if (action == MUIC_NOTIFY_CMD_ATTACH) + send_otg_notify(o_notify, NOTIFY_EVENT_SMARTDOCK_TA, 1); + else + pr_err("%s - ACTION Error!\n", __func__); + break; + case ATTACHED_DEV_SMARTDOCK_USB_MUIC: + if (action == MUIC_NOTIFY_CMD_DETACH) + send_otg_notify + (o_notify, NOTIFY_EVENT_SMARTDOCK_USB, 0); + else if (action == MUIC_NOTIFY_CMD_ATTACH) + send_otg_notify + (o_notify, NOTIFY_EVENT_SMARTDOCK_USB, 1); + else + pr_err("%s - ACTION Error!\n", __func__); + break; + case ATTACHED_DEV_AUDIODOCK_MUIC: + if (action == MUIC_NOTIFY_CMD_DETACH) + send_otg_notify(o_notify, NOTIFY_EVENT_AUDIODOCK, 0); + else if (action == MUIC_NOTIFY_CMD_ATTACH) + send_otg_notify(o_notify, NOTIFY_EVENT_AUDIODOCK, 1); + else + pr_err("%s - ACTION Error!\n", __func__); + break; + case ATTACHED_DEV_UNIVERSAL_MMDOCK_MUIC: + if (action == MUIC_NOTIFY_CMD_DETACH) + send_otg_notify(o_notify, NOTIFY_EVENT_MMDOCK, 0); + else if (action == MUIC_NOTIFY_CMD_ATTACH) + send_otg_notify(o_notify, NOTIFY_EVENT_MMDOCK, 1); + else + pr_err("%s - ACTION Error!\n", __func__); + break; + case ATTACHED_DEV_USB_LANHUB_MUIC: + if (action == MUIC_NOTIFY_CMD_DETACH) + send_otg_notify(o_notify, NOTIFY_EVENT_LANHUB, 0); + else if (action == MUIC_NOTIFY_CMD_ATTACH) + send_otg_notify(o_notify, NOTIFY_EVENT_LANHUB, 1); + else + pr_err("%s - ACTION Error!\n", __func__); + break; + case ATTACHED_DEV_GAMEPAD_MUIC: + if (action == MUIC_NOTIFY_CMD_DETACH) + send_otg_notify(o_notify, NOTIFY_EVENT_GAMEPAD, 0); + else if (action == MUIC_NOTIFY_CMD_ATTACH) + send_otg_notify(o_notify, NOTIFY_EVENT_GAMEPAD, 1); + else + pr_err("%s - ACTION Error!\n", __func__); + break; + default: + break; + } + + return 0; +} +#endif +#if defined(CONFIG_VBUS_NOTIFIER) +static int vbus_handle_notification(struct notifier_block *nb, + unsigned long cmd, void *data) +{ + vbus_status_t vbus_type = *(vbus_status_t *)data; + struct otg_notify *o_notify; + + o_notify = get_otg_notify(); + + pr_info("%s cmd=%lu, vbus_type=%s\n", + __func__, cmd, vbus_type == STATUS_VBUS_HIGH ? "HIGH" : "LOW"); + + switch (vbus_type) { + case STATUS_VBUS_HIGH: + send_otg_notify(o_notify, NOTIFY_EVENT_VBUSPOWER, 1); + break; + case STATUS_VBUS_LOW: + send_otg_notify(o_notify, NOTIFY_EVENT_VBUSPOWER, 0); + break; + default: + break; + } + return 0; +} +#endif + +static int otg_accessory_power(bool enable) +{ + u8 on = (u8)!!enable; + union power_supply_propval val; + + pr_info("otg accessory power = %d\n", on); + + val.intval = enable; + psy_do_property("otg", set, + POWER_SUPPLY_PROP_ONLINE, val); + + return 0; +} +#endif + +static int set_online(int event, int state) +{ + union power_supply_propval val; + struct device_node *np_charger = NULL; + char *charger_name; + + if (event == NOTIFY_EVENT_SMTD_EXT_CURRENT) + pr_info("request smartdock charging current = %s\n", + state ? "1000mA" : "1700mA"); + else if (event == NOTIFY_EVENT_MMD_EXT_CURRENT) + pr_info("request mmdock charging current = %s\n", + state ? "900mA" : "1400mA"); + + np_charger = of_find_node_by_name(NULL, "battery"); + if (!np_charger) { + pr_err("%s: failed to get the battery device node\n", __func__); + return 0; + } else { + if (!of_property_read_string(np_charger, "battery,charger_name", + (char const **)&charger_name)) { + pr_info("%s: charger_name = %s\n", __func__, + charger_name); + } else { + pr_err("%s: failed to get the charger name\n", + __func__); + return 0; + } + } + // for KNOX DT charging + pr_info("Knox Desktop connection state = %s\n", state ? "Connected" : "Disconnected"); +#if defined(CONFIG_IFCONN_NOTIFIER) + if (state) + val.intval = POWER_SUPPLY_TYPE_SMART_NOTG; + else + val.intval = POWER_SUPPLY_TYPE_BATTERY; +#else + if (state) + val.intval = SEC_BATTERY_CABLE_SMART_NOTG; + else + val.intval = SEC_BATTERY_CABLE_NONE; +#endif + + psy_do_property("battery", set, + POWER_SUPPLY_PROP_ONLINE, val); + + return 0; +} + +static int exynos_set_host(bool enable) +{ + if (!enable) { + pr_info("%s USB_HOST_DETACHED\n", __func__); +#ifdef CONFIG_OF + check_usb_id_state(1); +#endif + } else { + pr_info("%s USB_HOST_ATTACHED\n", __func__); +#ifdef CONFIG_OF + check_usb_id_state(0); +#endif + } + + return 0; +} + +static int exynos_set_peripheral(bool enable) +{ + if (enable) { + pr_info("%s usb attached\n", __func__); + check_usb_vbus_state(1); + } else { + pr_info("%s usb detached\n", __func__); + check_usb_vbus_state(0); + } + return 0; +} + +#if defined(CONFIG_BATTERY_SAMSUNG_V2) +static int usb_blocked_chg_control(int set) +{ + union power_supply_propval val; + struct device_node *np_charger = NULL; + char *charger_name; + + np_charger = of_find_node_by_name(NULL, "battery"); + if (!np_charger) { + pr_err("%s: failed to get the battery device node\n", __func__); + return 0; + } + + if (!of_property_read_string(np_charger, "battery,charger_name", + (char const **)&charger_name)) { + pr_info("%s: charger_name = %s\n", __func__, + charger_name); + } else { + pr_err("%s: failed to get the charger name\n", __func__); + return 0; + } + + /* current setting for upsm */ + pr_info("usb blocked : charing current set = %d\n", set); + + if (set) + val.intval = USB_CURRENT_HIGH_SPEED; + else + val.intval = USB_CURRENT_UNCONFIGURED; + + psy_do_property("battery", set, + POWER_SUPPLY_EXT_PROP_USB_CONFIGURE, val); + + return 0; + +} +#endif + +static struct otg_notify dwc_lsi_notify = { + .vbus_drive = otg_accessory_power, + .set_host = exynos_set_host, + .set_peripheral = exynos_set_peripheral, + .vbus_detect_gpio = -1, + .is_wakelock = 0, + .booting_delay_sec = 10, +#if !defined(CONFIG_CCIC_NOTIFIER) + .auto_drive_vbus = NOTIFY_OP_POST, +#endif + .disable_control = 1, + .device_check_sec = 3, + .set_battcall = set_online, +#if defined(CONFIG_CCIC_NOTIFIER) + .set_ldo_onoff = usb_regulator_onoff, +#endif +#if defined(CONFIG_BATTERY_SAMSUNG_V2) + .set_chg_current = usb_blocked_chg_control, +#endif + .pre_peri_delay_us = 6, +#if defined(CONFIG_USB_OTG_WHITELIST_FOR_MDM) + .sec_whitelist_enable = 0, +#endif +}; + +static int usb_notifier_probe(struct platform_device *pdev) +{ + struct usb_notifier_platform_data *pdata = NULL; + int ret = 0; + + if (pdev->dev.of_node) { + pdata = devm_kzalloc(&pdev->dev, + sizeof(struct usb_notifier_platform_data), GFP_KERNEL); + if (!pdata) { + dev_err(&pdev->dev, "Failed to allocate memory\n"); + return -ENOMEM; + } + + ret = of_usb_notifier_dt(&pdev->dev, pdata); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to get device of_node\n"); + return ret; + } + + pdev->dev.platform_data = pdata; + } else + pdata = pdev->dev.platform_data; + + dwc_lsi_notify.redriver_en_gpio = pdata->gpio_redriver_en; + dwc_lsi_notify.disable_control = pdata->can_disable_usb; + set_otg_notify(&dwc_lsi_notify); + set_notify_data(&dwc_lsi_notify, pdata); +#if defined(CONFIG_CCIC_NOTIFIER) + pdata->usb_ldo_onoff = 0; + INIT_DELAYED_WORK(&pdata->usb_ldo_work, + usb_ldo_off_control); + pdata->is_host = 0; +#endif + +#if defined(CONFIG_IFCONN_NOTIFIER) + ifconn_notifier_register(&pdata->muic_usb_nb, + muic_usb_handle_notification, + IFCONN_NOTIFY_USB, + IFCONN_NOTIFY_MUIC); + ifconn_notifier_register(&pdata->ccic_usb_nb, + ccic_usb_handle_notification, + IFCONN_NOTIFY_USB, + IFCONN_NOTIFY_CCIC); + ifconn_notifier_register(&pdata->vbus_nb, + vbus_handle_notification, + IFCONN_NOTIFY_USB, + IFCONN_NOTIFY_VBUS); +#else +#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER + manager_notifier_register(&pdata->ccic_usb_nb, ccic_usb_handle_notification, + MANAGER_NOTIFY_CCIC_USB); +#else + ccic_notifier_register(&pdata->ccic_usb_nb, ccic_usb_handle_notification, + CCIC_NOTIFY_DEV_USB); +#endif +#if defined(CONFIG_MUIC_NOTIFIER) + muic_notifier_register(&pdata->muic_usb_nb, muic_usb_handle_notification, + MUIC_NOTIFY_DEV_USB); +#endif +#if defined(CONFIG_VBUS_NOTIFIER) + vbus_notifier_register(&pdata->vbus_nb, vbus_handle_notification, + VBUS_NOTIFY_DEV_MANAGER); +#endif +#endif + + dev_info(&pdev->dev, "usb notifier probe\n"); + return 0; +} + +static int usb_notifier_remove(struct platform_device *pdev) +{ +#if defined(CONFIG_IFCONN_NOTIFIER) + ifconn_notifier_unregister(IFCONN_NOTIFY_USB, IFCONN_NOTIFY_CCIC); + ifconn_notifier_unregister(IFCONN_NOTIFY_USB, IFCONN_NOTIFY_MUIC); + ifconn_notifier_unregister(IFCONN_NOTIFY_USB, IFCONN_NOTIFY_VBUS); +#else + struct usb_notifier_platform_data *pdata = dev_get_platdata(&pdev->dev); +#if defined(CONFIG_CCIC_NOTIFIER) +#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER + manager_notifier_unregister(&pdata->ccic_usb_nb); +#else + ccic_notifier_unregister(&pdata->ccic_usb_nb); +#endif +#elif defined(CONFIG_MUIC_NOTIFIER) + muic_notifier_unregister(&pdata->muic_usb_nb); +#endif +#if defined(CONFIG_VBUS_NOTIFIER) + vbus_notifier_unregister(&pdata->vbus_nb); +#endif +#endif + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id usb_notifier_dt_ids[] = { + { .compatible = "samsung,usb-notifier", + }, + { }, +}; +MODULE_DEVICE_TABLE(of, usb_notifier_dt_ids); +#endif + +static struct platform_driver usb_notifier_driver = { + .probe = usb_notifier_probe, + .remove = usb_notifier_remove, + .driver = { + .name = "usb_notifier", + .owner = THIS_MODULE, +#ifdef CONFIG_OF + .of_match_table = of_match_ptr(usb_notifier_dt_ids), +#endif + }, +}; + +static int __init usb_notifier_init(void) +{ + return platform_driver_register(&usb_notifier_driver); +} + +static void __init usb_notifier_exit(void) +{ + platform_driver_unregister(&usb_notifier_driver); +} + +late_initcall(usb_notifier_init); +module_exit(usb_notifier_exit); + +MODULE_AUTHOR("inchul.im "); +MODULE_DESCRIPTION("USB notifier"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/notify/usb_notifier.h b/drivers/usb/notify/usb_notifier.h new file mode 100644 index 000000000000..487be0a39789 --- /dev/null +++ b/drivers/usb/notify/usb_notifier.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2014 Samsung Electronics Co. Ltd. + * + * 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. + */ +#ifndef __LINUX_USB_NOTIFIER_H__ +#define __LINUX_USB_NOTIFIER_H__ + +#ifdef CONFIG_USB_DWC3 +extern int dwc3_exynos_id_event(struct device *dev, int state); +extern int dwc3_exynos_vbus_event(struct device *dev, int state); +#else +static inline int dwc3_exynos_id_event + (struct device *dev, int state) {return 0; } +static inline int dwc3_exynos_vbus_event + (struct device *dev, int state) {return 0; } +#endif +#ifdef CONFIG_USB_S3C_OTGD +extern int exynos_otg_vbus_event(struct platform_device *pdev, int state); +#else +static inline int exynos_otg_vbus_event( + struct platform_device *pdev, int state) {return 0; } +#endif +#endif + diff --git a/drivers/usb/notify/usb_notify.c b/drivers/usb/notify/usb_notify.c new file mode 100644 index 000000000000..018aa2d98746 --- /dev/null +++ b/drivers/usb/notify/usb_notify.c @@ -0,0 +1,2027 @@ +/* + * Copyright (C) 2014-2017 Samsung Electronics Co. Ltd. + * + * 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. + */ + + /* usb notify layer v3.1 */ + +#define pr_fmt(fmt) "usb_notify: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dock_notify.h" +#include "usb_notify_sysfs.h" + +#define DEFAULT_OVC_POLL_SEC 3 + +struct ovc { + struct otg_notify *o_notify; + wait_queue_head_t delay_wait; + struct completion scanning_done; + struct task_struct *th; + struct mutex ovc_lock; + int thread_remove; + int can_ovc; + int poll_period; + int prev_state; + void *data; + int (*check_state)(void *); +}; + +struct vbus_gpio { + spinlock_t lock; + int gpio_status; +}; + +struct otg_state_work { + struct otg_notify *o_notify; + struct work_struct otg_work; + unsigned long event; + int enable; +}; + +struct otg_booting_delay { + struct delayed_work booting_work; + unsigned long reserve_state; +}; + +struct typec_info { + int data_role; + int power_role; +}; + +struct usb_notify { + struct otg_notify *o_notify; + struct atomic_notifier_head otg_notifier; + struct blocking_notifier_head extra_notifier; + struct notifier_block otg_nb; + struct notifier_block extra_nb; + struct vbus_gpio v_gpio; + struct host_notify_dev ndev; + struct usb_notify_dev udev; + struct workqueue_struct *notifier_wq; + struct wake_lock wlock; + struct otg_booster *booster; + struct ovc ovc_info; + struct otg_booting_delay b_delay; + struct delayed_work check_work; + struct typec_info typec_status; + int is_device; + int check_work_complete; + int oc_noti; + int disable_v_drive; + unsigned long c_type; + int c_status; +#if defined(CONFIG_USB_HW_PARAM) + unsigned long long hw_param[USB_CCIC_HW_PARAM_MAX]; +#endif +}; + +struct usb_notify_core { + struct otg_notify *o_notify; +}; + +static struct usb_notify_core *u_notify_core; + +/* + * Define event types. + * NOTIFY_EVENT_STATE can be called in both interrupt context + * and process context. But it executes queue_work. + * NOTIFY_EVENT_EXTRA can be called directly without queue_work. + * But it must be called in process context. + * NOTIFY_EVENT_DELAY events can not run inner booting delay. + * NOTIFY_EVENT_NEED_VBUSDRIVE events need to drive 5v out + * from phone charger ic + * NOTIFY_EVENT_NOBLOCKING events are not blocked by disable sysfs. + * NOTIFY_EVENT_NOSAVE events are not saved in cable type. + */ +static int check_event_type(enum otg_notify_events event) +{ + int ret = 0; + + switch (event) { + case NOTIFY_EVENT_OVERCURRENT: + case NOTIFY_EVENT_VBUSPOWER: + case NOTIFY_EVENT_SMSC_OVC: + case NOTIFY_EVENT_SMTD_EXT_CURRENT: + case NOTIFY_EVENT_MMD_EXT_CURRENT: + case NOTIFY_EVENT_DEVICE_CONNECT: + case NOTIFY_EVENT_GAMEPAD_CONNECT: + case NOTIFY_EVENT_LANHUB_CONNECT: + case NOTIFY_EVENT_POWER_SOURCE: + ret |= NOTIFY_EVENT_EXTRA; + break; + case NOTIFY_EVENT_VBUS: + case NOTIFY_EVENT_SMARTDOCK_USB: + ret |= (NOTIFY_EVENT_STATE | NOTIFY_EVENT_DELAY + | NOTIFY_EVENT_NEED_CLIENT); + break; + case NOTIFY_EVENT_HOST: + case NOTIFY_EVENT_HMT: + case NOTIFY_EVENT_GAMEPAD: + ret |= (NOTIFY_EVENT_STATE | NOTIFY_EVENT_NEED_VBUSDRIVE + | NOTIFY_EVENT_DELAY | NOTIFY_EVENT_NEED_HOST); + break; + case NOTIFY_EVENT_POGO: + ret |= (NOTIFY_EVENT_STATE | NOTIFY_EVENT_DELAY + | NOTIFY_EVENT_NEED_HOST); + break; + case NOTIFY_EVENT_ALL_DISABLE: + case NOTIFY_EVENT_HOST_DISABLE: + case NOTIFY_EVENT_CLIENT_DISABLE: + case NOTIFY_EVENT_MDM_ON_OFF: + ret |= (NOTIFY_EVENT_STATE | NOTIFY_EVENT_NOBLOCKING + | NOTIFY_EVENT_NOSAVE); + break; + case NOTIFY_EVENT_DRIVE_VBUS: + case NOTIFY_EVENT_LANHUB_TA: + ret |= (NOTIFY_EVENT_STATE | NOTIFY_EVENT_NOSAVE + | NOTIFY_EVENT_NEED_HOST); + break; + case NOTIFY_EVENT_SMARTDOCK_TA: + case NOTIFY_EVENT_AUDIODOCK: + case NOTIFY_EVENT_LANHUB: + case NOTIFY_EVENT_MMDOCK: + ret |= (NOTIFY_EVENT_DELAY | NOTIFY_EVENT_NEED_HOST); + case NOTIFY_EVENT_CHARGER: + case NOTIFY_EVENT_NONE: + default: + ret |= NOTIFY_EVENT_STATE; + break; + } + return ret; +} + +static int check_same_event_type(enum otg_notify_events event1, + enum otg_notify_events event2) +{ + return (check_event_type(event1) + == check_event_type(event2)); +} + +const char *event_string(enum otg_notify_events event) +{ + int virt; + + virt = IS_VIRTUAL(event); + event = PHY_EVENT(event); + + switch (event) { + case NOTIFY_EVENT_NONE: + return "none"; + case NOTIFY_EVENT_VBUS: + return virt ? "vbus(virtual)" : "vbus"; + case NOTIFY_EVENT_HOST: + return virt ? "host_id(virtual)" : "host_id"; + case NOTIFY_EVENT_CHARGER: + return virt ? "charger(virtual)" : "charger"; + case NOTIFY_EVENT_SMARTDOCK_TA: + return virt ? "smartdock_ta(virtual)" : "smartdock_ta"; + case NOTIFY_EVENT_SMARTDOCK_USB: + return virt ? "smartdock_usb(virtual)" : "smartdock_usb"; + case NOTIFY_EVENT_AUDIODOCK: + return virt ? "audiodock(virtual)" : "audiodock"; + case NOTIFY_EVENT_LANHUB: + return virt ? "lanhub(virtual)" : "lanhub"; + case NOTIFY_EVENT_LANHUB_TA: + return virt ? "lanhub_ta(virtual)" : "lanhub_ta"; + case NOTIFY_EVENT_MMDOCK: + return virt ? "mmdock(virtual)" : "mmdock"; + case NOTIFY_EVENT_HMT: + return virt ? "hmt(virtual)" : "hmt"; + case NOTIFY_EVENT_GAMEPAD: + return virt ? "gamepad(virtual)" : "gamepad"; + case NOTIFY_EVENT_POGO: + return virt ? "pogo(virtual)" : "pogo"; + case NOTIFY_EVENT_DRIVE_VBUS: + return "drive_vbus"; + case NOTIFY_EVENT_ALL_DISABLE: + return "disable_all_notify"; + case NOTIFY_EVENT_HOST_DISABLE: + return "disable_host_notify"; + case NOTIFY_EVENT_CLIENT_DISABLE: + return "disable_client_notify"; + case NOTIFY_EVENT_MDM_ON_OFF: + return "mdm control_notify"; + case NOTIFY_EVENT_OVERCURRENT: + return "overcurrent"; + case NOTIFY_EVENT_VBUSPOWER: + return "vbus_power"; + case NOTIFY_EVENT_SMSC_OVC: + return "smsc_ovc"; + case NOTIFY_EVENT_SMTD_EXT_CURRENT: + return "smtd_ext_current"; + case NOTIFY_EVENT_MMD_EXT_CURRENT: + return "mmd_ext_current"; + case NOTIFY_EVENT_DEVICE_CONNECT: + return "device_connect"; + case NOTIFY_EVENT_GAMEPAD_CONNECT: + return "gamepad_connect"; + case NOTIFY_EVENT_LANHUB_CONNECT: + return "lanhub_connect"; + case NOTIFY_EVENT_POWER_SOURCE: + return "power_role_source"; + default: + return "undefined"; + } +} +EXPORT_SYMBOL(event_string); + +const char *status_string(enum otg_notify_event_status status) +{ + switch (status) { + case NOTIFY_EVENT_DISABLED: + return "disabled"; + case NOTIFY_EVENT_DISABLING: + return "disabling"; + case NOTIFY_EVENT_ENABLED: + return "enabled"; + case NOTIFY_EVENT_ENABLING: + return "enabling"; + case NOTIFY_EVENT_BLOCKED: + return "blocked"; + case NOTIFY_EVENT_BLOCKING: + return "blocking"; + default: + return "undefined"; + } +} +EXPORT_SYMBOL(status_string); + +static const char *block_string(enum otg_notify_block_type type) +{ + switch (type) { + case NOTIFY_BLOCK_TYPE_NONE: + return "block_off"; + case NOTIFY_BLOCK_TYPE_HOST: + return "block_host"; + case NOTIFY_BLOCK_TYPE_CLIENT: + return "block_client"; + case NOTIFY_BLOCK_TYPE_ALL: + return "block_all"; + default: + return "undefined"; + } +} + +static bool is_host_cable_block(struct otg_notify *n) +{ + struct usb_notify *u_notify = (struct usb_notify *)(n->u_notify); + + if ((check_event_type(u_notify->c_type) + & NOTIFY_EVENT_NEED_HOST) && + (u_notify->c_status == NOTIFY_EVENT_BLOCKED + || u_notify->c_status == NOTIFY_EVENT_BLOCKING)) + return true; + else + return false; +} + +static bool is_host_cable_enable(struct otg_notify *n) +{ + struct usb_notify *u_notify = (struct usb_notify *)(n->u_notify); + + if ((check_event_type(u_notify->c_type) + & NOTIFY_EVENT_NEED_HOST) && + (u_notify->c_status == NOTIFY_EVENT_ENABLED + || u_notify->c_status == NOTIFY_EVENT_ENABLING)) + return true; + else + return false; +} + +static bool is_client_cable_block(struct otg_notify *n) +{ + struct usb_notify *u_notify = (struct usb_notify *)(n->u_notify); + + if ((check_event_type(u_notify->c_type) + & NOTIFY_EVENT_NEED_CLIENT) && + (u_notify->c_status == NOTIFY_EVENT_BLOCKED + || u_notify->c_status == NOTIFY_EVENT_BLOCKING)) + return true; + else + return false; +} + +static bool is_client_cable_enable(struct otg_notify *n) +{ + struct usb_notify *u_notify = (struct usb_notify *)(n->u_notify); + + if ((check_event_type(u_notify->c_type) + & NOTIFY_EVENT_NEED_CLIENT) && + (u_notify->c_status == NOTIFY_EVENT_ENABLED + || u_notify->c_status == NOTIFY_EVENT_ENABLING)) + return true; + else + return false; +} + +static bool check_block_event(struct otg_notify *n, unsigned long event) +{ + struct usb_notify *u_notify = (struct usb_notify *)(n->u_notify); + + if ((test_bit(NOTIFY_BLOCK_TYPE_HOST, &u_notify->udev.disable_state) + && (check_event_type(event) & NOTIFY_EVENT_NEED_HOST)) + || (test_bit(NOTIFY_BLOCK_TYPE_CLIENT, + &u_notify->udev.disable_state) + && (check_event_type(event) & NOTIFY_EVENT_NEED_CLIENT))) + return true; + else + return false; +} + +static void enable_ovc(struct usb_notify *u_noti, int enable) +{ + u_noti->ovc_info.can_ovc = enable; +} + +static int ovc_scan_thread(void *data) +{ + struct ovc *ovcinfo = (struct ovc *)data; + struct otg_notify *o_notify = ovcinfo->o_notify; + struct usb_notify *u_notify = (struct usb_notify *)(o_notify->u_notify); + int state; + + while (!kthread_should_stop()) { + wait_event_interruptible_timeout(ovcinfo->delay_wait, + ovcinfo->thread_remove, (ovcinfo->poll_period)*HZ); + if (ovcinfo->thread_remove) + break; + mutex_lock(&ovcinfo->ovc_lock); + if (ovcinfo->check_state + && ovcinfo->data + && ovcinfo->can_ovc) { + + state = ovcinfo->check_state(ovcinfo->data); + + if (ovcinfo->prev_state != state) { + if (state == HNOTIFY_LOW) { + pr_err("%s overcurrent detected\n", + __func__); + host_state_notify(&u_notify->ndev, + NOTIFY_HOST_OVERCURRENT); + } else if (state == HNOTIFY_HIGH) { + pr_info("%s vbus draw detected\n", + __func__); + host_state_notify(&u_notify->ndev, + NOTIFY_HOST_NONE); + } + } + ovcinfo->prev_state = state; + } + mutex_unlock(&ovcinfo->ovc_lock); + if (!ovcinfo->can_ovc) + ovcinfo->thread_remove = 1; + } + + pr_info("%s exit\n", __func__); + complete_and_exit(&ovcinfo->scanning_done, 0); + return 0; +} + +void ovc_start(struct usb_notify *u_noti) +{ + struct otg_notify *o_notify = u_noti->o_notify; + + if (!u_noti->ovc_info.can_ovc) + goto skip; + + u_noti->ovc_info.prev_state = HNOTIFY_INITIAL; + u_noti->ovc_info.poll_period = (o_notify->smsc_ovc_poll_sec) ? + o_notify->smsc_ovc_poll_sec : DEFAULT_OVC_POLL_SEC; + reinit_completion(&u_noti->ovc_info.scanning_done); + u_noti->ovc_info.thread_remove = 0; + u_noti->ovc_info.th = kthread_run(ovc_scan_thread, + &u_noti->ovc_info, "ovc-scan-thread"); + if (IS_ERR(u_noti->ovc_info.th)) { + pr_err("Unable to start the ovc-scanning thread\n"); + complete(&u_noti->ovc_info.scanning_done); + } + pr_info("%s on\n", __func__); + return; +skip: + complete(&u_noti->ovc_info.scanning_done); + pr_info("%s skip\n", __func__); +} + +void ovc_stop(struct usb_notify *u_noti) +{ + u_noti->ovc_info.thread_remove = 1; + wake_up_interruptible(&u_noti->ovc_info.delay_wait); + wait_for_completion(&u_noti->ovc_info.scanning_done); + mutex_lock(&u_noti->ovc_info.ovc_lock); + u_noti->ovc_info.check_state = NULL; + u_noti->ovc_info.data = 0; + mutex_unlock(&u_noti->ovc_info.ovc_lock); + pr_info("%s\n", __func__); +} + +static void ovc_init(struct usb_notify *u_noti) +{ + init_waitqueue_head(&u_noti->ovc_info.delay_wait); + init_completion(&u_noti->ovc_info.scanning_done); + mutex_init(&u_noti->ovc_info.ovc_lock); + u_noti->ovc_info.prev_state = HNOTIFY_INITIAL; + u_noti->ovc_info.o_notify = u_noti->o_notify; + pr_info("%s\n", __func__); +} + +static irqreturn_t vbus_irq_isr(int irq, void *data) +{ + struct otg_notify *notify = (struct otg_notify *)(data); + struct usb_notify *u_notify = (struct usb_notify *)(notify->u_notify); + unsigned long flags = 0; + int gpio_value = 0; + irqreturn_t ret = IRQ_NONE; + + spin_lock_irqsave(&u_notify->v_gpio.lock, flags); + gpio_value = gpio_get_value(notify->vbus_detect_gpio); + if (u_notify->v_gpio.gpio_status != gpio_value) { + u_notify->v_gpio.gpio_status = gpio_value; + ret = IRQ_WAKE_THREAD; + } else + ret = IRQ_HANDLED; + spin_unlock_irqrestore(&u_notify->v_gpio.lock, flags); + + return ret; +} + +static irqreturn_t vbus_irq_thread(int irq, void *data) +{ + struct otg_notify *notify = (struct otg_notify *)(data); + struct usb_notify *u_notify = (struct usb_notify *)(notify->u_notify); + unsigned long flags = 0; + int gpio_value = 0; + + spin_lock_irqsave(&u_notify->v_gpio.lock, flags); + gpio_value = u_notify->v_gpio.gpio_status; + spin_unlock_irqrestore(&u_notify->v_gpio.lock, flags); + + if (gpio_value) { + u_notify->ndev.booster = NOTIFY_POWER_ON; + pr_info("vbus on detect\n"); + if (notify->post_vbus_detect) + notify->post_vbus_detect(NOTIFY_POWER_ON); + } else { + if ((u_notify->ndev.mode == NOTIFY_HOST_MODE) + && (u_notify->ndev.booster == NOTIFY_POWER_ON + && u_notify->oc_noti)) { + host_state_notify(&u_notify->ndev, + NOTIFY_HOST_OVERCURRENT); + pr_err("OTG overcurrent!!!!!!\n"); + } else { + pr_info("vbus off detect\n"); + if (notify->post_vbus_detect) + notify->post_vbus_detect(NOTIFY_POWER_OFF); + } + u_notify->ndev.booster = NOTIFY_POWER_OFF; + } + return IRQ_HANDLED; +} + +int register_gpios(struct otg_notify *n) +{ + struct usb_notify *u_notify = (struct usb_notify *)(n->u_notify); + int ret = 0; + int vbus_irq = 0; + int vbus_gpio = -1; + int redriver_gpio = -1; + + if (!gpio_is_valid(n->vbus_detect_gpio)) + goto redriver_en_gpio_phase; + + vbus_gpio = n->vbus_detect_gpio; + + spin_lock_init(&u_notify->v_gpio.lock); + + if (n->pre_gpio) + n->pre_gpio(vbus_gpio, NOTIFY_VBUS); + + ret = gpio_request(vbus_gpio, "vbus_detect_notify"); + if (ret) { + pr_err("failed to request %d\n", vbus_gpio); + goto err; + } + gpio_direction_input(vbus_gpio); + + u_notify->v_gpio.gpio_status + = gpio_get_value(vbus_gpio); + vbus_irq = gpio_to_irq(vbus_gpio); + ret = request_threaded_irq(vbus_irq, + vbus_irq_isr, + vbus_irq_thread, + (IRQF_TRIGGER_FALLING | + IRQF_TRIGGER_RISING | + IRQF_ONESHOT), + "vbus_irq_notify", + n); + if (ret) { + pr_err("Failed to register IRQ\n"); + goto err; + } + if (n->post_gpio) + n->post_gpio(vbus_gpio, NOTIFY_VBUS); + + pr_info("vbus detect gpio %d is registered.\n", vbus_gpio); +redriver_en_gpio_phase: + if (!gpio_is_valid(n->redriver_en_gpio)) + goto err; + + redriver_gpio = n->redriver_en_gpio; + + if (n->pre_gpio) + n->pre_gpio(redriver_gpio, NOTIFY_REDRIVER); + + ret = gpio_request(redriver_gpio, "usb30_redriver_en"); + if (ret) { + pr_err("failed to request %d\n", redriver_gpio); + goto err; + } + gpio_direction_output(redriver_gpio, 0); + if (n->post_gpio) + n->post_gpio(redriver_gpio, NOTIFY_REDRIVER); + + pr_info("redriver en gpio %d is registered.\n", redriver_gpio); +err: + return ret; +} + +int do_notify_blockstate(struct otg_notify *n, unsigned long event, + int type, int enable) +{ + struct usb_notify *u_notify = (struct usb_notify *)(n->u_notify); + int ret = 0; + + switch (event) { + case NOTIFY_EVENT_NONE: + case NOTIFY_EVENT_CHARGER: + break; + case NOTIFY_EVENT_SMARTDOCK_USB: + case NOTIFY_EVENT_VBUS: + if (enable) + if (n->set_chg_current) + n->set_chg_current(1); + break; + case NOTIFY_EVENT_LANHUB: + case NOTIFY_EVENT_HMT: + case NOTIFY_EVENT_HOST: + case NOTIFY_EVENT_MMDOCK: + case NOTIFY_EVENT_SMARTDOCK_TA: + case NOTIFY_EVENT_AUDIODOCK: + case NOTIFY_EVENT_GAMEPAD: + case NOTIFY_EVENT_POGO: + if (n->unsupport_host) { + pr_err("This model doesn't support usb host\n"); + goto skip; + } + if (enable) + host_state_notify(&u_notify->ndev, NOTIFY_HOST_BLOCK); + else + host_state_notify(&u_notify->ndev, NOTIFY_HOST_NONE); + break; + case NOTIFY_EVENT_DRIVE_VBUS: + ret = -ESRCH; + break; + default: + break; + } + +skip: + return ret; +} + +static void update_cable_status(struct otg_notify *n, unsigned long event, + int virtual, int enable, int start) +{ + struct usb_notify *u_notify = (struct usb_notify *)(n->u_notify); + + if (enable) { + u_notify->c_type = event; + if (check_block_event(n, event) || + (check_event_type(u_notify->c_type) + & NOTIFY_EVENT_NEED_HOST && + n->unsupport_host)) + u_notify->c_status = (start) ? + NOTIFY_EVENT_BLOCKING : NOTIFY_EVENT_BLOCKED; + else + u_notify->c_status = (start) ? + NOTIFY_EVENT_ENABLING : NOTIFY_EVENT_ENABLED; + } else { + if (virtual) + u_notify->c_status = (start) ? + NOTIFY_EVENT_BLOCKING : NOTIFY_EVENT_BLOCKED; + else { + u_notify->c_type = NOTIFY_EVENT_NONE; + u_notify->c_status = (start) ? + NOTIFY_EVENT_DISABLING : NOTIFY_EVENT_DISABLED; + } + } +} + +static void otg_notify_state(struct otg_notify *n, + unsigned long event, int enable) +{ + struct usb_notify *u_notify = (struct usb_notify *)(n->u_notify); + int type = 0; + int virtual = 0; + unsigned long prev_c_type = 0; + + pr_info("%s+ event=%s(%lu), enable=%s\n", __func__, + event_string(event), event, enable == 0 ? "off" : "on"); + + prev_c_type = u_notify->c_type; + virtual = IS_VIRTUAL(event); + event = PHY_EVENT(event); + + type = check_event_type(event); + + if (!(type & NOTIFY_EVENT_NOSAVE)) { + update_cable_status(n, event, virtual, enable, 1); +#ifdef CONFIG_USB_NOTIFY_PROC_LOG + store_usblog_notify(NOTIFY_EVENT, + (void *)&event, (void *)&u_notify->c_status); +#endif + } + + if (check_block_event(n, event) && + !(type & NOTIFY_EVENT_NOBLOCKING)) { + pr_err("%s usb notify is blocked. cause %s\n", __func__, + u_notify->udev.disable_state_cmd); + if (do_notify_blockstate(n, event, type, enable)) + goto no_save_event; + else + goto err; + } + + switch (event) { + case NOTIFY_EVENT_NONE: + break; + case NOTIFY_EVENT_SMARTDOCK_USB: + case NOTIFY_EVENT_VBUS: + if (enable) { + u_notify->ndev.mode = NOTIFY_PERIPHERAL_MODE; + if (n->is_wakelock) + wake_lock(&u_notify->wlock); + if (gpio_is_valid(n->redriver_en_gpio)) + gpio_direction_output + (n->redriver_en_gpio, 1); + if (n->pre_peri_delay_us) + usleep_range(n->pre_peri_delay_us * 1000, + n->pre_peri_delay_us * 1000); + if (n->set_peripheral) + n->set_peripheral(true); + } else { + u_notify->ndev.mode = NOTIFY_NONE_MODE; + if (n->set_peripheral) + n->set_peripheral(false); + if (gpio_is_valid(n->redriver_en_gpio)) + gpio_direction_output + (n->redriver_en_gpio, 0); + if (n->is_wakelock) + wake_unlock(&u_notify->wlock); + } + break; + case NOTIFY_EVENT_LANHUB_TA: + u_notify->disable_v_drive = enable; + if (enable) + u_notify->oc_noti = 0; + if (n->set_lanhubta) + n->set_lanhubta(enable); + break; + case NOTIFY_EVENT_LANHUB: + if (n->unsupport_host) { + pr_err("This model doesn't support usb host\n"); + goto err; + } + u_notify->disable_v_drive = enable; + if (enable) { + u_notify->oc_noti = 0; + u_notify->ndev.mode = NOTIFY_HOST_MODE; + if (n->is_wakelock) + wake_lock(&u_notify->wlock); + host_state_notify(&u_notify->ndev, NOTIFY_HOST_ADD); + if (gpio_is_valid(n->redriver_en_gpio)) + gpio_direction_output + (n->redriver_en_gpio, 1); + if (n->set_host) + n->set_host(true); + } else { + u_notify->ndev.mode = NOTIFY_NONE_MODE; + if (n->set_host) + n->set_host(false); + if (gpio_is_valid(n->redriver_en_gpio)) + gpio_direction_output + (n->redriver_en_gpio, 0); + host_state_notify(&u_notify->ndev, NOTIFY_HOST_REMOVE); + if (n->is_wakelock) + wake_unlock(&u_notify->wlock); + } + break; + case NOTIFY_EVENT_HMT: + case NOTIFY_EVENT_HOST: + case NOTIFY_EVENT_GAMEPAD: + if (n->unsupport_host) { + pr_err("This model doesn't support usb host\n"); + goto err; + } + u_notify->disable_v_drive = 0; + if (enable) { + if (check_same_event_type(prev_c_type, event) + && !virtual) { + pr_err("now host mode, skip this command\n"); + goto err; + } + u_notify->ndev.mode = NOTIFY_HOST_MODE; + if (n->is_wakelock) + wake_lock(&u_notify->wlock); + host_state_notify(&u_notify->ndev, NOTIFY_HOST_ADD); + if (gpio_is_valid(n->redriver_en_gpio)) + gpio_direction_output + (n->redriver_en_gpio, 1); + if (n->auto_drive_vbus == NOTIFY_OP_PRE) { + u_notify->oc_noti = 1; + if (n->vbus_drive) + n->vbus_drive(1); + u_notify->typec_status.power_role + = HNOTIFY_SOURCE; + } + if (n->set_host) + n->set_host(true); + if (n->auto_drive_vbus == NOTIFY_OP_POST) { + u_notify->oc_noti = 1; + if (n->vbus_drive) + n->vbus_drive(1); + u_notify->typec_status.power_role + = HNOTIFY_SOURCE; + } + } else { /* disable */ + u_notify->ndev.mode = NOTIFY_NONE_MODE; + if (n->auto_drive_vbus == NOTIFY_OP_POST) { + u_notify->oc_noti = 0; + if (n->vbus_drive) + n->vbus_drive(0); + u_notify->typec_status.power_role + = HNOTIFY_SINK; + } + if (n->set_host) + n->set_host(false); + if (n->auto_drive_vbus == NOTIFY_OP_PRE) { + u_notify->oc_noti = 0; + if (n->vbus_drive) + n->vbus_drive(0); + u_notify->typec_status.power_role + = HNOTIFY_SINK; + } + if (gpio_is_valid(n->redriver_en_gpio)) + gpio_direction_output + (n->redriver_en_gpio, 0); + host_state_notify(&u_notify->ndev, NOTIFY_HOST_REMOVE); + if (n->is_wakelock) + wake_unlock(&u_notify->wlock); + } + break; + case NOTIFY_EVENT_CHARGER: + if (n->set_charger) + n->set_charger(enable); + break; + case NOTIFY_EVENT_MMDOCK: + enable_ovc(u_notify, enable); + /* To detect overcurrent, ndev state is initialized */ + if (enable) + host_state_notify(&u_notify->ndev, + NOTIFY_HOST_NONE); + case NOTIFY_EVENT_POGO: + case NOTIFY_EVENT_SMARTDOCK_TA: + case NOTIFY_EVENT_AUDIODOCK: + if (n->unsupport_host) { + pr_err("This model doesn't support usb host\n"); + goto err; + } + u_notify->disable_v_drive = enable; + if (enable) { + u_notify->ndev.mode = NOTIFY_HOST_MODE; + if (n->is_wakelock) + wake_lock(&u_notify->wlock); + if (n->set_host) + n->set_host(true); + } else { + u_notify->ndev.mode = NOTIFY_NONE_MODE; + if (n->set_host) + n->set_host(false); + if (n->is_wakelock) + wake_unlock(&u_notify->wlock); + } + break; + case NOTIFY_EVENT_DRIVE_VBUS: + if (n->unsupport_host) { + pr_err("This model doesn't support usb host\n"); + goto no_save_event; + } + if (u_notify->disable_v_drive) { + pr_info("cable type=%s disable vbus draw\n", + event_string(u_notify->c_type)); + goto no_save_event; + } + u_notify->oc_noti = enable; + if (n->vbus_drive) + n->vbus_drive((bool)enable); + goto no_save_event; + case NOTIFY_EVENT_ALL_DISABLE: + if (!n->disable_control) { + pr_err("This model doesn't support disable_control\n"); + goto no_save_event; + } + if (enable) { + send_external_notify(EXTERNAL_NOTIFY_HOSTBLOCK_PRE, 1); + + set_bit(NOTIFY_BLOCK_TYPE_HOST, + &u_notify->udev.disable_state); + set_bit(NOTIFY_BLOCK_TYPE_CLIENT, + &u_notify->udev.disable_state); + + send_external_notify(EXTERNAL_NOTIFY_HOSTBLOCK_POST, 1); + } else { + send_external_notify(EXTERNAL_NOTIFY_HOSTBLOCK_PRE, 0); + + clear_bit(NOTIFY_BLOCK_TYPE_HOST, + &u_notify->udev.disable_state); + clear_bit(NOTIFY_BLOCK_TYPE_CLIENT, + &u_notify->udev.disable_state); + + send_external_notify(EXTERNAL_NOTIFY_HOSTBLOCK_POST, 0); + } + goto no_save_event; + case NOTIFY_EVENT_HOST_DISABLE: + if (!n->disable_control) { + pr_err("This model doesn't support disable_control\n"); + goto no_save_event; + } + if (enable) { + send_external_notify(EXTERNAL_NOTIFY_HOSTBLOCK_PRE, 1); + + clear_bit(NOTIFY_BLOCK_TYPE_CLIENT, + &u_notify->udev.disable_state); + set_bit(NOTIFY_BLOCK_TYPE_HOST, + &u_notify->udev.disable_state); + + send_external_notify(EXTERNAL_NOTIFY_HOSTBLOCK_POST, 1); + } + goto no_save_event; + case NOTIFY_EVENT_CLIENT_DISABLE: + if (!n->disable_control) { + pr_err("This model doesn't support disable_control\n"); + goto no_save_event; + } + if (enable) { + clear_bit(NOTIFY_BLOCK_TYPE_HOST, + &u_notify->udev.disable_state); + set_bit(NOTIFY_BLOCK_TYPE_CLIENT, + &u_notify->udev.disable_state); + } + goto no_save_event; + case NOTIFY_EVENT_MDM_ON_OFF: + pr_info("%s : mdm block enable for usb whiltelist = %d\n", + __func__, enable); + if (enable) { + send_external_notify(EXTERNAL_NOTIFY_MDMBLOCK_PRE, 1); + /*whilte list start*/ + n->sec_whitelist_enable = 1; + send_external_notify(EXTERNAL_NOTIFY_MDMBLOCK_POST, 1); + } else { + /*whilte list end*/ + n->sec_whitelist_enable = 0; + } + goto no_save_event; + default: + break; + } + + if (((type & NOTIFY_EVENT_NEED_VBUSDRIVE) + && event != NOTIFY_EVENT_HOST) + || event == NOTIFY_EVENT_POGO) { + if (enable) { + if (n->device_check_sec) { + if (prev_c_type != NOTIFY_EVENT_HOST) + u_notify->is_device = 0; + u_notify->check_work_complete = 0; + schedule_delayed_work(&u_notify->check_work, + n->device_check_sec*HZ); + pr_info("%s check work start\n", __func__); + } + } else { + if (n->device_check_sec && + !u_notify->check_work_complete) { + pr_info("%s check work cancel\n", __func__); + cancel_delayed_work_sync(&u_notify->check_work); + } + u_notify->is_device = 0; + } + } +err: + update_cable_status(n, event, virtual, enable, 0); + +no_save_event: + pr_info("%s- event=%s, cable=%s\n", __func__, + event_string(event), + event_string(u_notify->c_type)); +} + +static void extra_notify_state(struct otg_notify *n, + unsigned long event, int enable) +{ + struct usb_notify *u_notify = (struct usb_notify *)(n->u_notify); + int status = 0; + + pr_info("%s+ event=%s(%lu), enable=%s\n", __func__, + event_string(event), event, enable == 0 ? "off" : "on"); + + switch (event) { + case NOTIFY_EVENT_NONE: + break; + case NOTIFY_EVENT_OVERCURRENT: + if (!u_notify->ndev.dev) { + pr_err("ndev is NULL. Maybe usb host is not supported.\n"); + break; + } + host_state_notify(&u_notify->ndev, + NOTIFY_HOST_OVERCURRENT); + pr_err("OTG overcurrent!!!!!!\n"); + break; + case NOTIFY_EVENT_VBUSPOWER: + if (enable) { + u_notify->ndev.booster = NOTIFY_POWER_ON; + status = NOTIFY_EVENT_ENABLED; + } else { + u_notify->ndev.booster = NOTIFY_POWER_OFF; + status = NOTIFY_EVENT_DISABLED; + } +#ifdef CONFIG_USB_NOTIFY_PROC_LOG + store_usblog_notify(NOTIFY_EVENT, + (void *)&event, (void *)&status); +#endif + break; + case NOTIFY_EVENT_SMSC_OVC: + if (enable) + ovc_start(u_notify); + else + ovc_stop(u_notify); + break; + case NOTIFY_EVENT_SMTD_EXT_CURRENT: + if (u_notify->c_type != NOTIFY_EVENT_SMARTDOCK_TA) { + pr_err("No smart dock!!!!!!\n"); + break; + } + if (n->set_battcall) + n->set_battcall + (NOTIFY_EVENT_SMTD_EXT_CURRENT, enable); + break; + case NOTIFY_EVENT_MMD_EXT_CURRENT: + if (u_notify->c_type != NOTIFY_EVENT_MMDOCK) { + pr_err("No mmdock!!!!!!\n"); + break; + } + if (n->set_battcall) + n->set_battcall + (NOTIFY_EVENT_MMD_EXT_CURRENT, enable); + break; + case NOTIFY_EVENT_DEVICE_CONNECT: + u_notify->is_device = 1; + break; + case NOTIFY_EVENT_GAMEPAD_CONNECT: + if (u_notify->c_type == NOTIFY_EVENT_HOST || + u_notify->c_type == NOTIFY_EVENT_GAMEPAD) + send_external_notify(EXTERNAL_NOTIFY_DEVICE_CONNECT, + EXTERNAL_NOTIFY_GPAD); + break; + case NOTIFY_EVENT_LANHUB_CONNECT: + if (u_notify->c_type == NOTIFY_EVENT_HOST || + u_notify->c_type == NOTIFY_EVENT_LANHUB) + send_external_notify(EXTERNAL_NOTIFY_DEVICE_CONNECT, + EXTERNAL_NOTIFY_LANHUB); + break; + case NOTIFY_EVENT_POWER_SOURCE: + if (enable) + u_notify->typec_status.power_role = HNOTIFY_SOURCE; + else + u_notify->typec_status.power_role = HNOTIFY_SINK; + send_external_notify(EXTERNAL_NOTIFY_POWERROLE, + u_notify->typec_status.power_role); + break; + default: + break; + } + pr_info("%s- event=%s(%lu), cable=%s\n", __func__, + event_string(event), event, + event_string(u_notify->c_type)); +} + +static void otg_notify_work(struct work_struct *data) +{ + struct otg_state_work *state_work = + container_of(data, struct otg_state_work, otg_work); + + otg_notify_state(state_work->o_notify, + state_work->event, state_work->enable); + + kfree(state_work); +} + +static int otg_notifier_callback(struct notifier_block *nb, + unsigned long event, void *param) +{ + struct usb_notify *u_noti = container_of(nb, + struct usb_notify, otg_nb); + struct otg_notify *n = NULL; + struct otg_state_work *state_work = NULL; + + n = u_noti->o_notify; + + pr_info("%s event=%s(%lu)\n", __func__, + event_string(event), event); + + if (event > VIRT_EVENT(NOTIFY_EVENT_VBUSPOWER)) { + pr_err("%s event is invalid\n", __func__); + return NOTIFY_DONE; + } + + state_work = kmalloc(sizeof(struct otg_state_work), GFP_ATOMIC); + if (!state_work) + return notifier_from_errno(-ENOMEM); + + INIT_WORK(&state_work->otg_work, otg_notify_work); + state_work->o_notify = n; + state_work->event = event; + state_work->enable = *(int *)param; + queue_work(u_noti->notifier_wq, &state_work->otg_work); + return NOTIFY_OK; +} + +static int extra_notifier_callback(struct notifier_block *nb, + unsigned long event, void *param) +{ + struct usb_notify *u_noti = container_of(nb, + struct usb_notify, extra_nb); + struct otg_notify *n = NULL; + + n = u_noti->o_notify; + + pr_info("%s event=%s(%lu)\n", __func__, + event_string(event), event); + + if (event > VIRT_EVENT(NOTIFY_EVENT_VBUSPOWER)) { + pr_err("%s event is invalid\n", __func__); + return NOTIFY_DONE; + } + + extra_notify_state(n, event, *(int *)param); + + return NOTIFY_OK; +} + +static int create_usb_notify(void) +{ + int ret = 0; + + if (u_notify_core) + goto err; + + u_notify_core = kzalloc(sizeof(struct usb_notify_core), GFP_KERNEL); + if (!u_notify_core) { + ret = -ENOMEM; + goto err; + } + + ret = usb_notify_class_init(); + if (ret) { + pr_err("unable to do usb_notify_class_init\n"); + goto err1; + } + return 0; +err1: + kfree(u_notify_core); +err: + return ret; +} + +static void reserve_state_check(struct work_struct *work) +{ + struct otg_booting_delay *o_b_d = container_of(work, + struct otg_booting_delay, booting_work.work); + struct usb_notify *u_noti = container_of(o_b_d, + struct usb_notify, b_delay); + int enable = 1; + unsigned long state = 0; + + state = u_noti->b_delay.reserve_state; + u_noti->o_notify->booting_delay_sec = 0; + pr_info("%s booting delay finished\n", __func__); + + if (u_noti->b_delay.reserve_state != NOTIFY_EVENT_NONE) { + pr_info("%s event=%s(%lu) enable=%d\n", __func__, + event_string(state), state, enable); + if (check_event_type(state) & NOTIFY_EVENT_EXTRA) + blocking_notifier_call_chain + (&u_noti->extra_notifier, + state, &enable); + else + atomic_notifier_call_chain + (&u_noti->otg_notifier, + state, &enable); + } +} + +static void device_connect_check(struct work_struct *work) +{ + struct usb_notify *u_notify = container_of(work, + struct usb_notify, check_work.work); + + pr_info("%s start. is_device=%d\n", __func__, u_notify->is_device); + if (!u_notify->is_device) { + send_external_notify(EXTERNAL_NOTIFY_3S_NODEVICE, 1); + + if (u_notify->o_notify->vbus_drive) + u_notify->o_notify->vbus_drive(0); + u_notify->typec_status.power_role = HNOTIFY_SINK; + } + u_notify->check_work_complete = 1; + pr_info("%s finished\n", __func__); +} + +int set_notify_disable(struct usb_notify_dev *udev, int disable) +{ + struct otg_notify *n = udev->o_notify; + struct usb_notify *u_notify = (struct usb_notify *)(n->u_notify); + +#ifdef CONFIG_USB_NOTIFY_PROC_LOG + unsigned long usb_notify; + int usb_notify_state; +#endif + + if (!n->disable_control) { + pr_err("%s disable_control is not supported\n", __func__); + goto skip; + } + + pr_info("%s disable=%s(%d)\n", __func__, + block_string(disable), disable); + + switch (disable) { + case NOTIFY_BLOCK_TYPE_ALL: + if (is_host_cable_enable(n) || + is_client_cable_enable(n)) { + + pr_info("%s event=%s(%lu) disable\n", __func__, + event_string(VIRT_EVENT(u_notify->c_type)), + VIRT_EVENT(u_notify->c_type)); + + if (!n->auto_drive_vbus && + (u_notify->typec_status.power_role + == HNOTIFY_SOURCE) && + check_event_type(u_notify->c_type) + & NOTIFY_EVENT_NEED_VBUSDRIVE) + send_otg_notify(n, NOTIFY_EVENT_DRIVE_VBUS, 0); + + send_otg_notify(n, VIRT_EVENT(u_notify->c_type), 0); + } + send_otg_notify(n, NOTIFY_EVENT_ALL_DISABLE, 1); +#ifdef CONFIG_USB_NOTIFY_PROC_LOG + usb_notify = NOTIFY_EVENT_ALL_DISABLE; + usb_notify_state = NOTIFY_EVENT_BLOCKED; + store_usblog_notify(NOTIFY_EVENT, + (void *)&usb_notify, (void *)&usb_notify_state); +#endif + break; + case NOTIFY_BLOCK_TYPE_HOST: + if (is_host_cable_enable(n)) { + + pr_info("%s event=%s(%lu) disable\n", __func__, + event_string(VIRT_EVENT(u_notify->c_type)), + VIRT_EVENT(u_notify->c_type)); + + if (!n->auto_drive_vbus && + (u_notify->typec_status.power_role + == HNOTIFY_SOURCE) && + check_event_type(u_notify->c_type) + & NOTIFY_EVENT_NEED_VBUSDRIVE) + send_otg_notify(n, NOTIFY_EVENT_DRIVE_VBUS, 0); + + send_otg_notify(n, VIRT_EVENT(u_notify->c_type), 0); + } + + send_otg_notify(n, NOTIFY_EVENT_HOST_DISABLE, 1); + +#ifdef CONFIG_USB_NOTIFY_PROC_LOG + usb_notify = NOTIFY_EVENT_HOST_DISABLE; + usb_notify_state = NOTIFY_EVENT_BLOCKED; + store_usblog_notify(NOTIFY_EVENT, + (void *)&usb_notify, (void *)&usb_notify_state); +#endif + + if (!is_client_cable_block(n)) + goto skip; + + pr_info("%s event=%s(%lu) enable\n", __func__, + event_string(VIRT_EVENT(u_notify->c_type)), + VIRT_EVENT(u_notify->c_type)); + + send_otg_notify(n, VIRT_EVENT(u_notify->c_type), 1); + break; + case NOTIFY_BLOCK_TYPE_CLIENT: + if (is_client_cable_enable(n)) { + + pr_info("%s event=%s(%lu) disable\n", __func__, + event_string(VIRT_EVENT(u_notify->c_type)), + VIRT_EVENT(u_notify->c_type)); + + send_otg_notify(n, VIRT_EVENT(u_notify->c_type), 0); + } + + send_otg_notify(n, NOTIFY_EVENT_CLIENT_DISABLE, 1); + +#ifdef CONFIG_USB_NOTIFY_PROC_LOG + usb_notify = NOTIFY_EVENT_CLIENT_DISABLE; + usb_notify_state = NOTIFY_EVENT_BLOCKED; + store_usblog_notify(NOTIFY_EVENT, + (void *)&usb_notify, (void *)&usb_notify_state); +#endif + + if (!is_host_cable_block(n)) + goto skip; + + if (n->unsupport_host) + goto skip; + + pr_info("%s event=%s(%lu) enable\n", __func__, + event_string(VIRT_EVENT(u_notify->c_type)), + VIRT_EVENT(u_notify->c_type)); + if (!n->auto_drive_vbus && + (u_notify->typec_status.power_role + == HNOTIFY_SOURCE) && + check_event_type(u_notify->c_type) + & NOTIFY_EVENT_NEED_VBUSDRIVE) + send_otg_notify(n, NOTIFY_EVENT_DRIVE_VBUS, 1); + + send_otg_notify(n, VIRT_EVENT(u_notify->c_type), 1); + break; + case NOTIFY_BLOCK_TYPE_NONE: + send_otg_notify(n, NOTIFY_EVENT_ALL_DISABLE, 0); +#ifdef CONFIG_USB_NOTIFY_PROC_LOG + usb_notify = NOTIFY_EVENT_ALL_DISABLE; + usb_notify_state = NOTIFY_EVENT_DISABLED; + store_usblog_notify(NOTIFY_EVENT, + (void *)&usb_notify, (void *)&usb_notify_state); +#endif + if (!is_host_cable_block(n) && !is_client_cable_block(n)) + goto skip; + + if (check_event_type(u_notify->c_type) + & NOTIFY_EVENT_NEED_HOST && n->unsupport_host) + goto skip; + + pr_info("%s event=%s(%lu) enable\n", __func__, + event_string(VIRT_EVENT(u_notify->c_type)), + VIRT_EVENT(u_notify->c_type)); + if (!n->auto_drive_vbus && + (u_notify->typec_status.power_role + == HNOTIFY_SOURCE) && + check_event_type(u_notify->c_type) + & NOTIFY_EVENT_NEED_VBUSDRIVE) + send_otg_notify(n, NOTIFY_EVENT_DRIVE_VBUS, 1); + + send_otg_notify(n, VIRT_EVENT(u_notify->c_type), 1); + break; + } +skip: + return 0; +} + +int get_class_index(int ch9_class_num) +{ + int internal_class_index; + + switch (ch9_class_num) { + case USB_CLASS_PER_INTERFACE: + internal_class_index = 1; + break; + case USB_CLASS_AUDIO: + internal_class_index = 2; + break; + case USB_CLASS_COMM: + internal_class_index = 3; + break; + case USB_CLASS_HID: + internal_class_index = 4; + break; + case USB_CLASS_PHYSICAL: + internal_class_index = 5; + break; + case USB_CLASS_STILL_IMAGE: + internal_class_index = 6; + break; + case USB_CLASS_PRINTER: + internal_class_index = 7; + break; + case USB_CLASS_MASS_STORAGE: + internal_class_index = 8; + break; + case USB_CLASS_HUB: + internal_class_index = 9; + break; + case USB_CLASS_CDC_DATA: + internal_class_index = 10; + break; + case USB_CLASS_CSCID: + internal_class_index = 11; + break; + case USB_CLASS_CONTENT_SEC: + internal_class_index = 12; + break; + case USB_CLASS_VIDEO: + internal_class_index = 13; + break; + case USB_CLASS_WIRELESS_CONTROLLER: + internal_class_index = 14; + break; + case USB_CLASS_MISC: + internal_class_index = 15; + break; + case USB_CLASS_APP_SPEC: + internal_class_index = 16; + break; + case USB_CLASS_VENDOR_SPEC: + internal_class_index = 17; + break; + default: + internal_class_index = 0; + break; + } + return internal_class_index; +} + +static bool usb_match_any_interface_for_mdm(struct usb_device *udev, + int *whitelist_array) +{ + unsigned int i; + + for (i = 0; i < udev->descriptor.bNumConfigurations; ++i) { + struct usb_host_config *cfg = &udev->config[i]; + unsigned int j; + + for (j = 0; j < cfg->desc.bNumInterfaces; ++j) { + struct usb_interface_cache *cache; + struct usb_host_interface *intf; + int intf_class; + + cache = cfg->intf_cache[j]; + if (cache->num_altsetting == 0) + continue; + + intf = &cache->altsetting[0]; + intf_class = intf->desc.bInterfaceClass; + if (!whitelist_array[get_class_index(intf_class)]) { + pr_info("%s : FAIL,%x interface, it's not in whitelist\n", + __func__, intf_class); + return false; + } + pr_info("%s : SUCCESS,%x interface, it's in whitelist\n", + __func__, intf_class); + } + } + return true; +} + +int usb_check_whitelist_for_mdm(struct usb_device *dev) +{ + int *whitelist_array; + struct otg_notify *o_notify; + struct usb_notify *u_notify; + /* return 1 if the enumeration will be going . */ + /* return 0 if the enumeration will be skept . */ + o_notify = get_otg_notify(); + if (o_notify == NULL) { + pr_err("o_notify is NULL\n"); + return 1; + } + + u_notify = (struct usb_notify *)(o_notify->u_notify); + if (u_notify == NULL) { + pr_err("u_notify is NULL\n"); + return 1; + } + + if (o_notify->sec_whitelist_enable) { + whitelist_array = u_notify->udev.whitelist_array_for_mdm; + if (usb_match_any_interface_for_mdm(dev, whitelist_array)) { + dev_info(&dev->dev, "the device is matched with whitelist!\n"); + return 1; + } + return 0; + } + return 1; +} + +void set_notify_mdm(struct usb_notify_dev *udev, int disable) +{ + struct otg_notify *n = udev->o_notify; + struct usb_notify *u_notify = (struct usb_notify *)(n->u_notify); + + switch (disable) { + case NOTIFY_MDM_TYPE_ON: + send_otg_notify(n, NOTIFY_EVENT_MDM_ON_OFF, 1); + if (is_host_cable_enable(n)) { + pr_info("%s event=%s(%lu) disable\n", __func__, + event_string(VIRT_EVENT(u_notify->c_type)), + VIRT_EVENT(u_notify->c_type)); + + if (!n->auto_drive_vbus && + (u_notify->typec_status.power_role + == HNOTIFY_SOURCE) + && check_event_type(u_notify->c_type) + & NOTIFY_EVENT_NEED_VBUSDRIVE) + send_otg_notify(n, NOTIFY_EVENT_DRIVE_VBUS, 0); + + send_otg_notify(n, VIRT_EVENT(u_notify->c_type), 0); + } + break; + case NOTIFY_MDM_TYPE_OFF: + send_otg_notify(n, NOTIFY_EVENT_MDM_ON_OFF, 0); + break; + } +} + +void send_otg_notify(struct otg_notify *n, + unsigned long event, int enable) +{ + struct usb_notify *u_notify = NULL; + int type = 0; + + if (!n) { + pr_err("%s otg_notify is null\n", __func__); + goto end; + } + + u_notify = (struct usb_notify *)(n->u_notify); + if (!u_notify) { + pr_err("%s u_notify structure is null\n", __func__); + goto end; + } + pr_info("%s event=%s(%lu) enable=%d\n", __func__, + event_string(event), event, enable); + + type = check_event_type(event); + + if (type & NOTIFY_EVENT_DELAY) { + if (n->booting_delay_sec) { + if (u_notify) { + u_notify->b_delay.reserve_state = + (enable) ? event : NOTIFY_EVENT_NONE; + pr_info("%s reserve event\n", __func__); + } else + pr_err("%s u_notify is null\n", __func__); + goto end; + } + } + + if (type & NOTIFY_EVENT_EXTRA) + blocking_notifier_call_chain + (&u_notify->extra_notifier, event, &enable); + else if (type & NOTIFY_EVENT_STATE) + atomic_notifier_call_chain + (&u_notify->otg_notifier, event, &enable); + else + goto end; +end: + return; +} +EXPORT_SYMBOL(send_otg_notify); + +void *get_notify_data(struct otg_notify *n) +{ + if (n) + return n->o_data; + else + return NULL; +} +EXPORT_SYMBOL(get_notify_data); + +void set_notify_data(struct otg_notify *n, void *data) +{ + n->o_data = data; +} +EXPORT_SYMBOL(set_notify_data); + +struct otg_notify *get_otg_notify(void) +{ + if (!u_notify_core) + return NULL; + if (!u_notify_core->o_notify) + return NULL; + return u_notify_core->o_notify; +} +EXPORT_SYMBOL(get_otg_notify); + +struct otg_booster *find_get_booster(struct otg_notify *n) +{ + struct usb_notify *u_notify = (struct usb_notify *)(n->u_notify); + int ret = 0; + + if (!u_notify) { + pr_err("%s u_notify structure is null\n", __func__); + goto err; + } + + if (!u_notify_core) { + ret = create_usb_notify(); + if (ret) { + pr_err("unable create_usb_notify\n"); + goto err; + } + } + + if (!u_notify->booster) { + pr_err("error. No matching booster\n"); + goto err; + } + + return u_notify->booster; +err: + return NULL; +} +EXPORT_SYMBOL(find_get_booster); + +int register_booster(struct otg_notify *n, struct otg_booster *b) +{ + struct usb_notify *u_notify = (struct usb_notify *)(n->u_notify); + int ret = 0; + + if (!u_notify) { + pr_err("%s u_notify structure is null\n", __func__); + goto err; + } + + u_notify->booster = b; +err: + return ret; +} +EXPORT_SYMBOL(register_booster); + +int register_ovc_func(struct otg_notify *n, + int (*check_state)(void *), void *data) +{ + struct usb_notify *u_notify; + int ret = 0; + + if (!n) { + pr_err("%s otg_notify is null\n", __func__); + return -ENODEV; + } + + u_notify = (struct usb_notify *)(n->u_notify); + if (!u_notify) { + pr_err("%s u_notify structure is null\n", __func__); + return -EFAULT; + } + + mutex_lock(&u_notify->ovc_info.ovc_lock); + u_notify->ovc_info.check_state = check_state; + u_notify->ovc_info.data = data; + mutex_unlock(&u_notify->ovc_info.ovc_lock); + pr_info("%s\n", __func__); + return ret; +} +EXPORT_SYMBOL(register_ovc_func); + +int get_usb_mode(struct otg_notify *n) +{ + struct usb_notify *u_notify; + int ret = 0; + + if (!n) { + pr_err("%s otg_notify is null\n", __func__); + return -ENODEV; + } + + u_notify = (struct usb_notify *)(n->u_notify); + if (!u_notify) { + pr_err("%s u_notify structure is null\n", __func__); + return NOTIFY_NONE_MODE; + } + + if (!u_notify_core) { + ret = create_usb_notify(); + if (ret) { + pr_err("unable create_usb_notify\n"); + return -EFAULT; + } + } + pr_info("%s usb mode=%d\n", __func__, u_notify->ndev.mode); + ret = u_notify->ndev.mode; + return ret; +} +EXPORT_SYMBOL(get_usb_mode); + +unsigned long get_cable_type(struct otg_notify *n) +{ + struct usb_notify *u_notify = (struct usb_notify *)(n->u_notify); + unsigned long ret = 0; + int noti_ret = 0; + + if (!u_notify) { + pr_err("%s u_notify structure is null\n", __func__); + return NOTIFY_EVENT_NONE; + } + + if (!u_notify_core) { + noti_ret = create_usb_notify(); + if (noti_ret) { + pr_err("unable create_usb_notify\n"); + return NOTIFY_EVENT_NONE; + } + } + pr_info("%s cable type =%s\n", __func__, + event_string(u_notify->c_type)); + ret = u_notify->c_type; + return ret; +} +EXPORT_SYMBOL(get_cable_type); + +int is_usb_host(struct otg_notify *n) +{ + struct usb_notify *u_notify = (struct usb_notify *)(n->u_notify); + int ret = 0; + int noti_ret = 0; + + if (!u_notify) { + pr_err("%s u_notify structure is null\n", __func__); + return NOTIFY_EVENT_NONE; + } + + if (!u_notify_core) { + noti_ret = create_usb_notify(); + if (noti_ret) { + pr_err("unable create_usb_notify\n"); + return NOTIFY_EVENT_NONE; + } + } + + if (n->unsupport_host || !IS_ENABLED(CONFIG_USB_HOST_NOTIFY)) + ret = 0; + else + ret = 1; + + pr_info("%s %d\n", __func__, ret); + return ret; +} +EXPORT_SYMBOL(is_usb_host); + +int set_otg_notify(struct otg_notify *n) +{ + struct usb_notify *u_notify; + int ret = 0; + + if (!u_notify_core) { + ret = create_usb_notify(); + if (ret) { + pr_err("unable create_usb_notify\n"); + goto err; + } + } + + if (u_notify_core->o_notify && n) { + pr_err("error : already set o_notify\n"); + goto err; + } + + pr_info("registered otg_notify +\n"); + if (!n) { + pr_err("otg notify structure is null\n"); + ret = -EFAULT; + goto err1; + } + u_notify_core->o_notify = n; + + u_notify = kzalloc(sizeof(struct usb_notify), GFP_KERNEL); + if (!u_notify) { + ret = -ENOMEM; + goto err1; + } + + u_notify->o_notify = n; + + n->u_notify = (void *)u_notify; + + u_notify->notifier_wq + = create_singlethread_workqueue("usb_notify"); + if (!u_notify->notifier_wq) { + pr_err("%s failed to create work queue\n", __func__); + ret = -ENOMEM; + goto err2; + } + + ovc_init(u_notify); + + ATOMIC_INIT_NOTIFIER_HEAD(&u_notify->otg_notifier); + u_notify->otg_nb.notifier_call = otg_notifier_callback; + ret = atomic_notifier_chain_register(&u_notify->otg_notifier, + &u_notify->otg_nb); + if (ret < 0) { + pr_err("atomic_notifier_chain_register failed\n"); + goto err3; + } + + BLOCKING_INIT_NOTIFIER_HEAD(&u_notify->extra_notifier); + u_notify->extra_nb.notifier_call = extra_notifier_callback; + ret = blocking_notifier_chain_register + (&u_notify->extra_notifier, &u_notify->extra_nb); + if (ret < 0) { + pr_err("blocking_notifier_chain_register failed\n"); + goto err4; + } + + if (!n->unsupport_host) { + u_notify->ndev.name = "usb_otg"; + u_notify->ndev.set_booster = n->vbus_drive; + ret = host_notify_dev_register(&u_notify->ndev); + if (ret < 0) { + pr_err("host_notify_dev_register is failed\n"); + goto err5; + } + + if (!n->vbus_drive) { + pr_err("vbus_drive is null\n"); + goto err6; + } + } + + u_notify->udev.name = "usb_control"; + u_notify->udev.set_disable = set_notify_disable; + u_notify->udev.set_mdm = set_notify_mdm; + u_notify->udev.o_notify = n; + + ret = usb_notify_dev_register(&u_notify->udev); + if (ret < 0) { + pr_err("usb_notify_dev_register is failed\n"); + goto err6; + } + + if (gpio_is_valid(n->vbus_detect_gpio) || + gpio_is_valid(n->redriver_en_gpio)) { + ret = register_gpios(n); + if (ret < 0) { + pr_err("register_gpios is failed\n"); + goto err7; + } + } + + if (n->is_wakelock) + wake_lock_init(&u_notify->wlock, + WAKE_LOCK_SUSPEND, "usb_notify"); + + if (n->booting_delay_sec) { + INIT_DELAYED_WORK(&u_notify->b_delay.booting_work, + reserve_state_check); + schedule_delayed_work(&u_notify->b_delay.booting_work, + n->booting_delay_sec*HZ); + } + + if (n->device_check_sec) + INIT_DELAYED_WORK(&u_notify->check_work, + device_connect_check); + + register_usbdev_notify(); + + register_usblog_proc(); + + pr_info("registered otg_notify -\n"); + return 0; +err7: + usb_notify_dev_unregister(&u_notify->udev); +err6: + if (!n->unsupport_host) + host_notify_dev_unregister(&u_notify->ndev); +err5: + blocking_notifier_chain_unregister(&u_notify->extra_notifier, + &u_notify->extra_nb); +err4: + atomic_notifier_chain_unregister(&u_notify->otg_notifier, + &u_notify->otg_nb); +err3: + flush_workqueue(u_notify->notifier_wq); + destroy_workqueue(u_notify->notifier_wq); +err2: + kfree(u_notify); +err1: + u_notify_core->o_notify = NULL; +err: + return ret; +} +EXPORT_SYMBOL(set_otg_notify); + +void put_otg_notify(struct otg_notify *n) +{ + struct usb_notify *u_notify = (struct usb_notify *)(n->u_notify); + + if (!u_notify) { + pr_err("%s u_notify structure is null\n", __func__); + return; + } + unregister_usblog_proc(); + unregister_usbdev_notify(); + if (n->booting_delay_sec) + cancel_delayed_work_sync(&u_notify->b_delay.booting_work); + if (n->is_wakelock) + wake_lock_destroy(&u_notify->wlock); + if (gpio_is_valid(n->vbus_detect_gpio)) + free_irq(gpio_to_irq(n->vbus_detect_gpio), NULL); + usb_notify_dev_unregister(&u_notify->udev); + if (!n->unsupport_host) + host_notify_dev_unregister(&u_notify->ndev); + blocking_notifier_chain_unregister(&u_notify->extra_notifier, + &u_notify->extra_nb); + atomic_notifier_chain_unregister(&u_notify->otg_notifier, + &u_notify->otg_nb); + flush_workqueue(u_notify->notifier_wq); + destroy_workqueue(u_notify->notifier_wq); + u_notify->o_notify = NULL; + kfree(u_notify); +} +EXPORT_SYMBOL(put_otg_notify); + +bool is_blocked(struct otg_notify *n, int type) +{ + struct usb_notify *u_notify = NULL; + int ret = 0; + + if (!n) { + pr_err("%s otg_notify is null\n", __func__); + goto end; + } + u_notify = (struct usb_notify *)(n->u_notify); + + if (!u_notify) { + pr_err("%s u_notify structure is null\n", __func__); + goto end; + } + + if (!u_notify_core) { + ret = create_usb_notify(); + if (ret) { + pr_err("unable create_usb_notify\n"); + goto end; + } + } + pr_info("%s type=%d, disable_state=%lu\n", + __func__, type, u_notify->udev.disable_state); + + if (type == NOTIFY_BLOCK_TYPE_HOST) { + if (test_bit(NOTIFY_BLOCK_TYPE_HOST, + &u_notify->udev.disable_state)) + goto end2; + } else if (type == NOTIFY_BLOCK_TYPE_CLIENT) { + if (test_bit(NOTIFY_BLOCK_TYPE_CLIENT, + &u_notify->udev.disable_state)) + goto end2; + } else if (type == NOTIFY_BLOCK_TYPE_ALL) { + if (test_bit(NOTIFY_BLOCK_TYPE_HOST, + &u_notify->udev.disable_state) && + test_bit(NOTIFY_BLOCK_TYPE_CLIENT, + &u_notify->udev.disable_state)) + goto end2; + } + +end: + return false; +end2: + return true; +} +EXPORT_SYMBOL(is_blocked); + +#if defined(CONFIG_USB_HW_PARAM) +unsigned long long *get_hw_param(struct otg_notify *n, + enum usb_hw_param index) +{ + struct usb_notify *u_notify = (struct usb_notify *)(n->u_notify); + int ret = 0; + + if (index < 0 || index >= USB_CCIC_HW_PARAM_MAX) { + pr_err("%s usb_hw_param is out of bound\n", __func__); + return NULL; + } + + if (!u_notify) { + pr_err("%s u_notify structure is null\n", __func__); + return NULL; + } + + if (!u_notify_core) { + ret = create_usb_notify(); + if (ret) { + pr_err("unable create_usb_notify\n"); + return NULL; + } + } + return &(u_notify->hw_param[index]); +} +EXPORT_SYMBOL(get_hw_param); + +int inc_hw_param(struct otg_notify *n, + enum usb_hw_param index) +{ + struct usb_notify *u_notify; + int ret = 0; + + if (!n) { + pr_err("%s otg_notify is null\n", __func__); + return -ENODEV; + } + + u_notify = (struct usb_notify *)(n->u_notify); + + if (index < 0 || index >= USB_CCIC_HW_PARAM_MAX) { + pr_err("%s usb_hw_param is out of bound\n", __func__); + ret = -ENOMEM; + return ret; + } + + if (!u_notify) { + pr_err("%s u_notify structure is null\n", __func__); + ret = -ENOENT; + return ret; + } + + if (!u_notify_core) { + ret = create_usb_notify(); + if (ret) { + pr_err("unable create_usb_notify\n"); + return ret; + } + } + u_notify->hw_param[index]++; + return ret; +} +EXPORT_SYMBOL(inc_hw_param); + +int inc_hw_param_host(struct host_notify_dev *dev, + enum usb_hw_param index) +{ + struct usb_notify *u_notify = container_of(dev, + struct usb_notify, ndev); + int ret = 0; + + if (index < 0 || index >= USB_CCIC_HW_PARAM_MAX) { + pr_err("%s usb_hw_param is out of bound\n", __func__); + ret = -ENOMEM; + return ret; + } + + if (!u_notify) { + pr_err("%s u_notify structure is null\n", __func__); + ret = -ENOENT; + return ret; + } + + if (!u_notify_core) { + ret = create_usb_notify(); + if (ret) { + pr_err("unable create_usb_notify\n"); + return ret; + } + } + u_notify->hw_param[index]++; + return ret; +} +EXPORT_SYMBOL(inc_hw_param_host); +#endif + +static int __init usb_notify_init(void) +{ + return create_usb_notify(); +} + +static void __exit usb_notify_exit(void) +{ + if (!u_notify_core) + return; + usb_notify_class_exit(); +} + +module_init(usb_notify_init); +module_exit(usb_notify_exit); + +MODULE_AUTHOR("Samsung USB Team"); +MODULE_DESCRIPTION("USB Notify Layer"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/notify/usb_notify_sysfs.c b/drivers/usb/notify/usb_notify_sysfs.c new file mode 100644 index 000000000000..65bda87bd6f7 --- /dev/null +++ b/drivers/usb/notify/usb_notify_sysfs.c @@ -0,0 +1,700 @@ +/* + * drivers/usb/notify/usb_notify_sysfs.c + * + * Copyright (C) 2015-2017 Samsung, Inc. + * Author: Dongrak Shin + * +*/ + + /* usb notify layer v3.1 */ + +#define pr_fmt(fmt) "usb_notify: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "usb_notify_sysfs.h" + +#if defined(CONFIG_USB_HW_PARAM) +const char +usb_hw_param_print[USB_CCIC_HW_PARAM_MAX][MAX_HWPARAM_STRING] = { + {"CC_WATER"}, + {"CC_DRY"}, + {"CC_I2C"}, + {"CC_OVC"}, + {"CC_OTG"}, + {"CC_DP"}, + {"CC_VR"}, + {"H_SUPER"}, + {"H_HIGH"}, + {"H_FULL"}, + {"H_LOW"}, + {"C_SUPER"}, + {"C_HIGH"}, + {"H_AUDIO"}, + {"H_COMM"}, + {"H_HID"}, + {"H_PHYSIC"}, + {"H_IMAGE"}, + {"H_PRINTER"}, + {"H_STORAGE"}, + {"H_HUB"}, + {"H_CDC"}, + {"H_CSCID"}, + {"H_CONTENT"}, + {"H_VIDEO"}, + {"H_WIRE"}, + {"H_MISC"}, + {"H_APP"}, + {"H_VENDOR"}, + {"CC_DEX"}, + {"CC_WTIME"}, + {"CC_WVBUS"}, + {"CC_WVTIME"}, + {"CC_CSHORT"}, + {"M_AFCNAK"}, + {"M_AFCERR"}, + {"M_DCDTMO"}, + {"CC_VER"}, +}; +#endif + +struct notify_data { + struct class *usb_notify_class; + atomic_t device_count; +}; + +static struct notify_data usb_notify_data; + +static int is_valid_cmd(char *cur_cmd, char *prev_cmd) +{ + pr_info("%s : current state=%s, previous state=%s\n", + __func__, cur_cmd, prev_cmd); + + if (!strcmp(cur_cmd, "ON") || + !strncmp(cur_cmd, "ON_ALL_", 7)) { + if (!strcmp(prev_cmd, "ON") || + !strncmp(prev_cmd, "ON_ALL_", 7)) { + goto ignore; + } else if (!strncmp(prev_cmd, "ON_HOST_", 8)) { + goto all; + } else if (!strncmp(prev_cmd, "ON_CLIENT_", 10)) { + goto all; + } else if (!strcmp(prev_cmd, "OFF")) { + goto all; + } else { + goto invalid; + } + } else if (!strcmp(cur_cmd, "OFF")) { + if (!strcmp(prev_cmd, "ON") || + !strncmp(prev_cmd, "ON_ALL_", 7)) { + goto off; + } else if (!strncmp(prev_cmd, "ON_HOST_", 8)) { + goto off; + } else if (!strncmp(prev_cmd, "ON_CLIENT_", 10)) { + goto off; + } else if (!strcmp(prev_cmd, "OFF")) { + goto ignore; + } else { + goto invalid; + } + } else if (!strncmp(cur_cmd, "ON_HOST_", 8)) { + if (!strcmp(prev_cmd, "ON") || + !strncmp(prev_cmd, "ON_ALL_", 7)) { + goto host; + } else if (!strncmp(prev_cmd, "ON_HOST_", 8)) { + goto ignore; + } else if (!strncmp(prev_cmd, "ON_CLIENT_", 10)) { + goto host; + } else if (!strcmp(prev_cmd, "OFF")) { + goto host; + } else { + goto invalid; + } + } else if (!strncmp(cur_cmd, "ON_CLIENT_", 10)) { + if (!strcmp(prev_cmd, "ON") || + !strncmp(prev_cmd, "ON_ALL_", 7)) { + goto client; + } else if (!strncmp(prev_cmd, "ON_HOST_", 8)) { + goto client; + } else if (!strncmp(prev_cmd, "ON_CLIENT_", 10)) { + goto ignore; + } else if (!strcmp(prev_cmd, "OFF")) { + goto client; + } else { + goto invalid; + } + } else { + goto invalid; + } +host: + pr_info("%s cmd=%s is accepted.\n", __func__, cur_cmd); + return NOTIFY_BLOCK_TYPE_HOST; +client: + pr_info("%s cmd=%s is accepted.\n", __func__, cur_cmd); + return NOTIFY_BLOCK_TYPE_CLIENT; +all: + pr_info("%s cmd=%s is accepted.\n", __func__, cur_cmd); + return NOTIFY_BLOCK_TYPE_ALL; +off: + pr_info("%s cmd=%s is accepted.\n", __func__, cur_cmd); + return NOTIFY_BLOCK_TYPE_NONE; +ignore: + pr_err("%s cmd=%s is ignored but saved.\n", __func__, cur_cmd); + return -EEXIST; +invalid: + pr_err("%s cmd=%s is invalid.\n", __func__, cur_cmd); + return -EINVAL; +} + +static ssize_t disable_show( + struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct usb_notify_dev *udev = (struct usb_notify_dev *) + dev_get_drvdata(dev); + + pr_info("read disable_state %s\n", udev->disable_state_cmd); + return sprintf(buf, "%s\n", udev->disable_state_cmd); +} + +static ssize_t disable_store( + struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + struct usb_notify_dev *udev = (struct usb_notify_dev *) + dev_get_drvdata(dev); + + char *disable; + int sret, param = -EINVAL; + size_t ret = -ENOMEM; + + if (size > MAX_DISABLE_STR_LEN) { + pr_err("%s size(%zu) is too long.\n", __func__, size); + goto error; + } + + disable = kzalloc(size+1, GFP_KERNEL); + if (!disable) + goto error; + + sret = sscanf(buf, "%s", disable); + if (sret != 1) + goto error1; + + if (udev->set_disable) { + param = is_valid_cmd(disable, udev->disable_state_cmd); + if (param == -EINVAL) { + ret = param; + } else { + if (param != -EEXIST) + udev->set_disable(udev, param); + strncpy(udev->disable_state_cmd, + disable, sizeof(udev->disable_state_cmd)-1); + ret = size; + } + } else + pr_err("set_disable func is NULL\n"); +error1: + kfree(disable); +error: + return ret; +} + +static ssize_t support_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct usb_notify_dev *udev = (struct usb_notify_dev *) + dev_get_drvdata(dev); + struct otg_notify *n = udev->o_notify; + char *support; + + if (n->unsupport_host || !IS_ENABLED(CONFIG_USB_HOST_NOTIFY)) + support = "CLIENT"; + else + support = "ALL"; + + pr_info("read support %s\n", support); + return snprintf(buf, sizeof(support)+1, "%s\n", support); +} + +static ssize_t otg_speed_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct usb_notify_dev *udev = (struct usb_notify_dev *) + dev_get_drvdata(dev); + struct otg_notify *n = udev->o_notify; + char *speed; + + switch (n->speed) { + case USB_SPEED_SUPER: + speed = "SUPER"; + break; + case USB_SPEED_HIGH: + speed = "HIGH"; + break; + case USB_SPEED_FULL: + speed = "FULL"; + break; + case USB_SPEED_LOW: + speed = "LOW"; + break; + default: + speed = "UNKNOWN"; + break; + } + pr_info("%s : read otg speed %s\n", __func__, speed); + return snprintf(buf, sizeof(speed)+1, "%s\n", speed); +} + +#if defined(CONFIG_USB_HW_PARAM) +static unsigned long long strtoull(char *ptr, char **end, int base) +{ + unsigned long long ret = 0; + + if (base > 36) + goto out; + + while (*ptr) { + int digit; + + if (*ptr >= '0' && *ptr <= '9' && *ptr < '0' + base) + digit = *ptr - '0'; + else if (*ptr >= 'A' && *ptr < 'A' + base - 10) + digit = *ptr - 'A' + 10; + else if (*ptr >= 'a' && *ptr < 'a' + base - 10) + digit = *ptr - 'a' + 10; + else + break; + + ret *= base; + ret += digit; + ptr++; + } + +out: + if (end) + *end = (char *)ptr; + + return ret; +} + +static ssize_t usb_hw_param_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct usb_notify_dev *udev = (struct usb_notify_dev *) + dev_get_drvdata(dev); + struct otg_notify *n = udev->o_notify; + int index, ret = 0; + unsigned long long *p_param = NULL; +#if defined(CONFIG_USB_TYPEC_MANAGER_NOTIFIER) + p_param = get_hw_param(n, USB_CCIC_WATER_INT_COUNT); + if (p_param) + *p_param += get_ccic_water_count(); + p_param = get_hw_param(n, USB_CCIC_DRY_INT_COUNT); + if (p_param) + *p_param += get_ccic_dry_count(); + p_param = get_hw_param(n, USB_CLIENT_SUPER_SPEED_COUNT); + if (p_param) + *p_param += get_usb310_count(); + p_param = get_hw_param(n, USB_CLIENT_HIGH_SPEED_COUNT); + if (p_param) + *p_param += get_usb210_count(); + p_param = get_hw_param(n, USB_CCIC_WATER_TIME_DURATION); + if (p_param) + *p_param += get_waterDet_duration(); + p_param = get_hw_param(n, USB_CCIC_WATER_VBUS_COUNT); + if (p_param) + *p_param += get_waterChg_count(); + p_param = get_hw_param(n, USB_CCIC_WATER_VBUS_TIME_DURATION); + if (p_param) + *p_param += get_wVbus_duration(); + p_param = get_hw_param(n, USB_CCIC_VERSION); +#if defined(CONFIG_USB_NOTIFY_PROC_LOG) + if (p_param) + *p_param = show_ccic_version(); +#endif +#endif + for (index = 0; index < USB_CCIC_HW_PARAM_MAX - 1; index++) { + p_param = get_hw_param(n, index); + if (p_param) + ret += sprintf(buf + ret, "%llu ", *p_param); + else + ret += sprintf(buf + ret, "0 "); + } + p_param = get_hw_param(n, index); + if (p_param) + ret += sprintf(buf + ret, "%llu\n", *p_param); + else + ret += sprintf(buf + ret, "0\n"); + pr_info("%s - ret : %d\n", __func__, ret); + + return ret; +} + +static ssize_t usb_hw_param_store( + struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + struct usb_notify_dev *udev = (struct usb_notify_dev *) + dev_get_drvdata(dev); + struct otg_notify *n = udev->o_notify; + unsigned long long prev_hw_param[USB_CCIC_HW_PARAM_MAX] = {0, }; + + int index = 0; + size_t ret = -ENOMEM; + char *token, *str = (char *)buf; + + if (size > MAX_HWPARAM_STR_LEN) { + pr_err("%s size(%zu) is too long.\n", __func__, size); + goto error; + } + ret = size; + if (size < USB_CCIC_HW_PARAM_MAX) { + pr_err("%s efs file is not created correctly.\n", __func__); + goto error; + } + + for (index = 0; index < (USB_CCIC_HW_PARAM_MAX - 1); index++) { + token = strsep(&str, " "); + if (token) + prev_hw_param[index] = strtoull(token, NULL, 10); + + if (!token || (prev_hw_param[index] > HWPARAM_DATA_LIMIT)) + goto error; + } + + for (index = 0; index < (USB_CCIC_HW_PARAM_MAX - 1); index++) + *(get_hw_param(n, index)) += prev_hw_param[index]; + pr_info("%s - ret : %zu\n", __func__, ret); +error: + return ret; +} + +static ssize_t hw_param_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct usb_notify_dev *udev = (struct usb_notify_dev *) + dev_get_drvdata(dev); + struct otg_notify *n = udev->o_notify; + int index, ret = 0; + unsigned long long *p_param = NULL; +#if defined(CONFIG_USB_TYPEC_MANAGER_NOTIFIER) + p_param = get_hw_param(n, USB_CCIC_WATER_INT_COUNT); + if (p_param) + *p_param += get_ccic_water_count(); + p_param = get_hw_param(n, USB_CCIC_DRY_INT_COUNT); + if (p_param) + *p_param += get_ccic_dry_count(); + p_param = get_hw_param(n, USB_CLIENT_SUPER_SPEED_COUNT); + if (p_param) + *p_param += get_usb310_count(); + p_param = get_hw_param(n, USB_CLIENT_HIGH_SPEED_COUNT); + if (p_param) + *p_param += get_usb210_count(); + p_param = get_hw_param(n, USB_CCIC_WATER_TIME_DURATION); + if (p_param) + *p_param += get_waterDet_duration(); + p_param = get_hw_param(n, USB_CCIC_WATER_VBUS_COUNT); + if (p_param) + *p_param += get_waterChg_count(); + p_param = get_hw_param(n, USB_CCIC_WATER_VBUS_TIME_DURATION); + if (p_param) + *p_param += get_wVbus_duration(); + p_param = get_hw_param(n, USB_CCIC_VERSION); +#if defined(CONFIG_USB_NOTIFY_PROC_LOG) + if (p_param) + *p_param = show_ccic_version(); +#endif +#endif + for (index = 0; index < USB_CCIC_HW_PARAM_MAX - 1; index++) { + p_param = get_hw_param(n, index); + if (p_param) + ret += sprintf(buf + ret, "\"%s\":\"%llu\",", + usb_hw_param_print[index], *p_param); + else + ret += sprintf(buf + ret, "\"%s\":\"0\",", + usb_hw_param_print[index]); + } + /* CCIC FW version */ + ret += sprintf(buf + ret, "\"%s\":\"", + usb_hw_param_print[index]); + p_param = get_hw_param(n, index); + if (p_param) { + /* HW Version */ + ret += sprintf(buf + ret, "%02X%02X%02X%02X", + *((unsigned char *)p_param + 3), + *((unsigned char *)p_param + 2), + *((unsigned char *)p_param + 1), + *((unsigned char *)p_param)); + /* SW Main Version */ + ret += sprintf(buf + ret, "%02X%02X%02X", + *((unsigned char *)p_param + 6), + *((unsigned char *)p_param + 5), + *((unsigned char *)p_param + 4)); + /* SW Boot Version */ + ret += sprintf(buf + ret, "%02X", + *((unsigned char *)p_param + 7)); + ret += sprintf(buf + ret, "\"\n"); + } else + ret += sprintf(buf + ret, "0000000000000000\"\n"); + + pr_info("%s - ret : %d\n", __func__, ret); + return ret; +} + +static ssize_t hw_param_store( + struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + struct usb_notify_dev *udev = (struct usb_notify_dev *) + dev_get_drvdata(dev); + struct otg_notify *n = udev->o_notify; + int index = 0; + size_t ret = -ENOMEM; + char *str = (char *)buf; + + if (size > 2) { + pr_err("%s size(%zu) is too long.\n", __func__, size); + goto error; + } + ret = size; + pr_info("%s : %s\n", __func__, str); + if (!strncmp(str, "c", 1)) + for (index = 0; index < USB_CCIC_HW_PARAM_MAX; index++) + *(get_hw_param(n, index)) = 0; +error: + return ret; +} +#endif + +char interface_class_name[USB_CLASS_VENDOR_SPEC][4] = { + {"PER"}, + {"AUD"}, + {"COM"}, + {"HID"}, + {"PHY"}, + {"STI"}, + {"PRI"}, + {"MAS"}, + {"HUB"}, + {"CDC"}, + {"CSC"}, + {"CON"}, + {"VID"}, + {"WIR"}, + {"MIS"}, + {"APP"}, + {"VEN"} +}; + +void init_usb_whitelist_array(int *whitelist_array) +{ + int i; + + for (i = 1; i <= MAX_CLASS_TYPE_NUM; i++) + whitelist_array[i] = 0; +} + +int set_usb_whitelist_array(const char *buf, int *whitelist_array) +{ + int valid_class_count = 0; + char *ptr = NULL; + int i; + char *source; + + source = (char *)buf; + while ((ptr = strsep(&source, ":")) != NULL) { + pr_info("%s token = %c%c%c!\n", __func__, + ptr[0], ptr[1], ptr[2]); + for (i = 1; i <= USB_CLASS_VENDOR_SPEC; i++) { + if (!strncmp(ptr, interface_class_name[i-1], 3)) + whitelist_array[i] = 1; + } + } + + for (i = 1; i <= U_CLASS_VENDOR_SPEC; i++) { + if (whitelist_array[i]) + valid_class_count++; + } + pr_info("%s valid_class_count = %d!\n", __func__, valid_class_count); + return valid_class_count; +} + +static ssize_t whitelist_for_mdm_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct usb_notify_dev *udev = (struct usb_notify_dev *) + dev_get_drvdata(dev); + + if (udev == NULL) { + pr_err("udev is NULL\n"); + return -EINVAL; + } + pr_info("%s read whitelist_classes %s\n", + __func__, udev->whitelist_str); + return sprintf(buf, "%s\n", udev->whitelist_str); +} + +static ssize_t whitelist_for_mdm_store( + struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + struct usb_notify_dev *udev = (struct usb_notify_dev *) + dev_get_drvdata(dev); + char *disable; + int sret; + size_t ret = -ENOMEM; + int mdm_disable; + int valid_whilelist_count; + + if (udev == NULL) { + pr_err("udev is NULL\n"); + ret = -EINVAL; + goto error; + } + + if (size > MAX_WHITELIST_STR_LEN) { + pr_err("%s size(%zu) is too long.\n", __func__, size); + goto error; + } + + disable = kzalloc(size+1, GFP_KERNEL); + if (!disable) + goto error; + + sret = sscanf(buf, "%s", disable); + if (sret != 1) + goto error1; + pr_info("%s buf=%s\n", __func__, disable); + + init_usb_whitelist_array(udev->whitelist_array_for_mdm); + /* To active displayport, hub class must be enabled */ + if (!strncmp(buf, "ABL", 3)) { + udev->whitelist_array_for_mdm[USB_CLASS_HUB] = 1; + mdm_disable = NOTIFY_MDM_TYPE_ON; + } else if (!strncmp(buf, "OFF", 3)) + mdm_disable = NOTIFY_MDM_TYPE_OFF; + else { + valid_whilelist_count = set_usb_whitelist_array + (buf, udev->whitelist_array_for_mdm); + if (valid_whilelist_count > 0) { + udev->whitelist_array_for_mdm[USB_CLASS_HUB] = 1; + mdm_disable = NOTIFY_MDM_TYPE_ON; + } else + mdm_disable = NOTIFY_MDM_TYPE_OFF; + } + + strncpy(udev->whitelist_str, + disable, sizeof(udev->whitelist_str)-1); + + if (udev->set_mdm) { + udev->set_mdm(udev, mdm_disable); + ret = size; + } else { + pr_err("set_mdm func is NULL\n"); + ret = -EINVAL; + } +error1: + kfree(disable); +error: + return ret; +} + +static DEVICE_ATTR(disable, 0664, disable_show, disable_store); +static DEVICE_ATTR(support, 0444, support_show, NULL); +static DEVICE_ATTR(otg_speed, 0444, otg_speed_show, NULL); +static DEVICE_ATTR(whitelist_for_mdm, 0664, + whitelist_for_mdm_show, whitelist_for_mdm_store); +#if defined(CONFIG_USB_HW_PARAM) +static DEVICE_ATTR(usb_hw_param, 0664, usb_hw_param_show, usb_hw_param_store); +static DEVICE_ATTR(hw_param, 0664, hw_param_show, hw_param_store); +#endif + +static struct attribute *usb_notify_attrs[] = { + &dev_attr_disable.attr, + &dev_attr_support.attr, + &dev_attr_otg_speed.attr, + &dev_attr_whitelist_for_mdm.attr, +#if defined(CONFIG_USB_HW_PARAM) + &dev_attr_usb_hw_param.attr, + &dev_attr_hw_param.attr, +#endif + NULL, +}; + +static struct attribute_group usb_notify_attr_grp = { + .attrs = usb_notify_attrs, +}; + +static int create_usb_notify_class(void) +{ + if (!usb_notify_data.usb_notify_class) { + usb_notify_data.usb_notify_class + = class_create(THIS_MODULE, "usb_notify"); + if (IS_ERR(usb_notify_data.usb_notify_class)) + return PTR_ERR(usb_notify_data.usb_notify_class); + atomic_set(&usb_notify_data.device_count, 0); + } + + return 0; +} + +int usb_notify_dev_register(struct usb_notify_dev *udev) +{ + int ret; + + if (!usb_notify_data.usb_notify_class) { + ret = create_usb_notify_class(); + if (ret < 0) + return ret; + } + + udev->index = atomic_inc_return(&usb_notify_data.device_count); + udev->dev = device_create(usb_notify_data.usb_notify_class, NULL, + MKDEV(0, udev->index), NULL, udev->name); + if (IS_ERR(udev->dev)) + return PTR_ERR(udev->dev); + + udev->disable_state = 0; + strncpy(udev->disable_state_cmd, "OFF", + sizeof(udev->disable_state_cmd)-1); + ret = sysfs_create_group(&udev->dev->kobj, &usb_notify_attr_grp); + if (ret < 0) { + device_destroy(usb_notify_data.usb_notify_class, + MKDEV(0, udev->index)); + return ret; + } + + dev_set_drvdata(udev->dev, udev); + return 0; +} +EXPORT_SYMBOL_GPL(usb_notify_dev_register); + +void usb_notify_dev_unregister(struct usb_notify_dev *udev) +{ + sysfs_remove_group(&udev->dev->kobj, &usb_notify_attr_grp); + device_destroy(usb_notify_data.usb_notify_class, MKDEV(0, udev->index)); + dev_set_drvdata(udev->dev, NULL); +} +EXPORT_SYMBOL_GPL(usb_notify_dev_unregister); + +int usb_notify_class_init(void) +{ + return create_usb_notify_class(); +} +EXPORT_SYMBOL_GPL(usb_notify_class_init); + +void usb_notify_class_exit(void) +{ + class_destroy(usb_notify_data.usb_notify_class); +} +EXPORT_SYMBOL_GPL(usb_notify_class_exit); + diff --git a/drivers/usb/notify/usb_notify_sysfs.h b/drivers/usb/notify/usb_notify_sysfs.h new file mode 100644 index 000000000000..d001eccacc17 --- /dev/null +++ b/drivers/usb/notify/usb_notify_sysfs.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2015-2017 Samsung Electronics Co. Ltd. + * + * 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. + */ + + /* usb notify layer v3.1 */ + +#ifndef __LINUX_USB_NOTIFY_SYSFS_H__ +#define __LINUX_USB_NOTIFY_SYSFS_H__ + +#define MAX_DISABLE_STR_LEN 32 +#define MAX_WHITELIST_STR_LEN 256 +#define MAX_CLASS_TYPE_NUM USB_CLASS_VENDOR_SPEC + +enum u_interface_class_type { + U_CLASS_PER_INTERFACE = 1, + U_CLASS_AUDIO, + U_CLASS_COMM, + U_CLASS_HID, + U_CLASS_PHYSICAL, + U_CLASS_STILL_IMAGE, + U_CLASS_PRINTER, + U_CLASS_MASS_STORAGE, + U_CLASS_HUB, + U_CLASS_CDC_DATA, + U_CLASS_CSCID, + U_CLASS_CONTENT_SEC, + U_CLASS_VIDEO, + U_CLASS_WIRELESS_CONTROLLER, + U_CLASS_MISC, + U_CLASS_APP_SPEC, + U_CLASS_VENDOR_SPEC, +}; + +struct usb_notify_dev { + const char *name; + struct device *dev; + struct otg_notify *o_notify; + int index; + unsigned long disable_state; + char disable_state_cmd[MAX_DISABLE_STR_LEN]; + int (*set_disable)(struct usb_notify_dev *, int); + void (*set_mdm)(struct usb_notify_dev *udev, int mdm_disable); + char whitelist_str[MAX_WHITELIST_STR_LEN]; + int whitelist_array_for_mdm[MAX_CLASS_TYPE_NUM+1]; +}; + +extern int usb_notify_dev_register(struct usb_notify_dev *ndev); +extern void usb_notify_dev_unregister(struct usb_notify_dev *ndev); +extern int usb_notify_class_init(void); +extern void usb_notify_class_exit(void); +#endif + diff --git a/drivers/usb/notify/usblog_proc_notify.c b/drivers/usb/notify/usblog_proc_notify.c new file mode 100644 index 000000000000..25a2329ec60f --- /dev/null +++ b/drivers/usb/notify/usblog_proc_notify.c @@ -0,0 +1,1059 @@ +/* + * drivers/usb/notify/usblog_proc_notify.c + * + * Copyright (C) 2016-2017 Samsung, Inc. + * Author: Dongrak Shin + * +*/ + + /* usb notify layer v3.1 */ + + #define pr_fmt(fmt) "usb_notify: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define USBLOG_MAX_BUF_SIZE (1 << 7) /* 128 */ +#define USBLOG_MAX_BUF2_SIZE (1 << 8) /* 256 */ +#define USBLOG_MAX_BUF3_SIZE (1 << 9) /* 512 */ +#define USBLOG_MAX_STRING_SIZE (1 << 4) /* 16 */ +#define USBLOG_CMP_INDEX 3 + +#define USBLOG_CCIC_BUFFER_SIZE USBLOG_MAX_BUF3_SIZE +#define USBLOG_MODE_BUFFER_SIZE USBLOG_MAX_BUF_SIZE +#define USBLOG_STATE_BUFFER_SIZE USBLOG_MAX_BUF2_SIZE +#define USBLOG_EVENT_BUFFER_SIZE USBLOG_MAX_BUF_SIZE + +struct ccic_buf { + unsigned long long ts_nsec; + int cc_type; + uint64_t noti; +}; + +struct mode_buf { + unsigned long long ts_nsec; + char usbmode_str[USBLOG_MAX_STRING_SIZE]; +}; + +struct state_buf { + unsigned long long ts_nsec; + int usbstate; +}; + +struct event_buf { + unsigned long long ts_nsec; + unsigned long event; + int enable; +}; + +struct usblog_buf { + unsigned long long ccic_count; + unsigned long long mode_count; + unsigned long long state_count; + unsigned long long event_count; + unsigned long ccic_index; + unsigned long mode_index; + unsigned long state_index; + unsigned long event_index; + struct ccic_buf ccic_buffer[USBLOG_CCIC_BUFFER_SIZE]; + struct mode_buf mode_buffer[USBLOG_MODE_BUFFER_SIZE]; + struct state_buf state_buffer[USBLOG_STATE_BUFFER_SIZE]; + struct event_buf event_buffer[USBLOG_EVENT_BUFFER_SIZE]; +}; + +struct ccic_version { + unsigned char hw_version[4]; + unsigned char sw_main[3]; + unsigned char sw_boot; +}; + +struct usblog_root_str { + struct usblog_buf *usblog_buffer; + struct ccic_version ccic_ver; + spinlock_t usblog_lock; + int init; +}; + +struct ccic_type { + uint64_t src:4; + uint64_t dest:4; + uint64_t id:8; + uint64_t sub1:16; + uint64_t sub2:16; + uint64_t sub3:16; +}; + +static struct usblog_root_str usblog_root; + +static const char *usbstate_string(enum usblog_state usbstate) +{ + switch (usbstate) { + case NOTIFY_CONFIGURED: + return "CONFIGURED"; + case NOTIFY_CONNECTED: + return "CONNECTED"; + case NOTIFY_DISCONNECTED: + return "DISCONNECTED"; + case NOTIFY_RESET: + return "RESET"; + case NOTIFY_RESET_FULL: + return "RESET : FULL"; + case NOTIFY_RESET_HIGH: + return "RESET: HIGH"; + case NOTIFY_RESET_SUPER: + return "RESET : SUPER"; + case NOTIFY_PULLUP: + return "VBUS_PULLUP (EN OR DIS)"; + case NOTIFY_PULLUP_ENABLE: + return "VBUS_PULLUP_EN"; + case NOTIFY_PULLUP_EN_SUCCESS: + return "VBUS_PULLUP_EN : S"; + case NOTIFY_PULLUP_EN_FAIL: + return "VBUS_PULLUP_EN : F"; + case NOTIFY_PULLUP_DISABLE: + return "VBUS_PULLUP_DIS"; + case NOTIFY_PULLUP_DIS_SUCCESS: + return "VBUS_PULLUP_DIS : S"; + case NOTIFY_PULLUP_DIS_FAIL: + return "VBUS_PULLUP_DIS : F"; + case NOTIFY_VBUS_SESSION: + return "VBUS_SESSION (EN OR DIS)"; + case NOTIFY_VBUS_SESSION_ENABLE: + return "VBUS_SESSION_EN"; + case NOTIFY_VBUS_EN_SUCCESS: + return "VBUS_SESSION_EN : S"; + case NOTIFY_VBUS_EN_FAIL: + return "VBUS_SESSION_EN : F"; + case NOTIFY_VBUS_SESSION_DISABLE: + return "VBUS_SESSIOIN_DIS"; + case NOTIFY_VBUS_DIS_SUCCESS: + return "VBUS_SESSION_DIS : S"; + case NOTIFY_VBUS_DIS_FAIL: + return "VBUS_SESSION_DIS : F"; + case NOTIFY_ACCSTART: + return "ACCSTART"; + case NOTIFY_HIGH: + return "HIGH SPEED"; + case NOTIFY_SUPER: + return "SUPER SPEED"; + default: + return "UNDEFINED"; + } +} + +static const char *usbstatus_string(enum usblog_status usbstatus) +{ + switch (usbstatus) { + case NOTIFY_DETACH: + return "DETACH"; + case NOTIFY_ATTACH_DFP: + return "ATTACH_DFP"; + case NOTIFY_ATTACH_UFP: + return "ATTACH_UFP"; + case NOTIFY_ATTACH_DRP: + return "ATTACH_DRP"; + default: + return "UNDEFINED"; + } +} + +static const char *ccic_dev_string(enum ccic_device dev) +{ + switch (dev) { + case NOTIFY_DEV_INITIAL: + return "INITIAL"; + case NOTIFY_DEV_USB: + return "USB"; + case NOTIFY_DEV_BATTERY: + return "BATTERY"; + case NOTIFY_DEV_PDIC: + return "PDIC"; + case NOTIFY_DEV_MUIC: + return "MUIC"; + case NOTIFY_DEV_CCIC: + return "CCIC"; +#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER + case NOTIFY_DEV_MANAGER: + return "MANAGER"; +#endif + case NOTIFY_DEV_DP: + return "DP"; + case NOTIFY_DEV_USB_DP: + return "USB_DP"; + default: + return "UNDEFINED"; + } +} + +static const char *ccic_id_string(enum ccic_id id) +{ + switch (id) { + case NOTIFY_ID_INITIAL: + return "ID_INITIAL"; + case NOTIFY_ID_ATTACH: + return "ID_CONNECT"; + case NOTIFY_ID_RID: + return "ID_RID"; + case NOTIFY_ID_USB: + return "ID_USB"; +#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER + case NOTIFY_ID_POWER_STATUS: + return "ID_POWER_STATUS"; +#endif + case NOTIFY_ID_WATER: + return "ID_WATER"; + case NOTIFY_ID_VCONN: + return "ID_VCONN"; + case NOTIFY_ID_DP_CONNECT: + return "ID_DP_CONNECT"; + case NOTIFY_ID_DP_HPD: + return "ID_DP_HPD"; + case NOTIFY_ID_DP_LINK_CONF: + return "ID_DP_LINK_CONF"; + case NOTIFY_ID_USB_DP: + return "ID_USB_DP"; + case NOTIFY_ID_ROLE_SWAP: + return "ID_ROLE_SWAP"; + default: + return "UNDEFINED"; + } +} + +static const char *ccic_rid_string(enum ccic_rid rid) +{ + switch (rid) { + case NOTIFY_RID_UNDEFINED: + return "RID_UNDEFINED"; + case NOTIFY_RID_000K: + return "RID_000K"; + case NOTIFY_RID_001K: + return "RID_001K"; + case NOTIFY_RID_255K: + return "RID_255K"; + case NOTIFY_RID_301K: + return "RID_301K"; + case NOTIFY_RID_523K: + return "RID_523K"; + case NOTIFY_RID_619K: + return "RID_619K"; + case NOTIFY_RID_OPEN: + return "RID_OPEN"; + default: + return "UNDEFINED"; + } +} + +static const char *ccic_con_string(enum ccic_con con) +{ + switch (con) { + case NOTIFY_CON_DETACH: + return "DETACHED"; + case NOTIFY_CON_ATTACH: + return "ATTACHED"; + default: + return "UNDEFINED"; + } +} + +static const char *ccic_rprd_string(enum ccic_rprd rprd) +{ + switch (rprd) { + case NOTIFY_RD: + return "RD"; + case NOTIFY_RP: + return "RP"; + default: + return "UNDEFINED"; + } +} + +static const char *ccic_rpstatus_string(enum ccic_rpstatus rprd) +{ + switch (rprd) { + case NOTIFY_RP_NONE: + return "NONE"; + case NOTIFY_RP_56K: + return "RP_56K"; + case NOTIFY_RP_22K: + return "RP_22K"; + case NOTIFY_RP_10K: + return "RP_10K"; + case NOTIFY_RP_ABNORMAL: + return "RP_ABNORMAL"; + default: + return "UNDEFINED"; + } +} + +static const char *ccic_hpd_string(enum ccic_hpd hpd) +{ + switch (hpd) { + case NOTIFY_HPD_LOW: + return "LOW"; + case NOTIFY_HPD_HIGH: + return "HIGH"; + case NOTIFY_HPD_IRQ: + return "IRQ"; + default: + return "UNDEFINED"; + } +} + +static const char *ccic_pinA_string(enum ccic_pin_assignment pin) +{ + switch (pin) { + case NOTIFY_DP_PIN_UNKNOWN: + return "UNKNOWN"; + case NOTIFY_DP_PIN_A: + return "DP_PIN_A"; + case NOTIFY_DP_PIN_B: + return "DP_PIN_B"; + case NOTIFY_DP_PIN_C: + return "DP_PIN_C"; + case NOTIFY_DP_PIN_D: + return "DP_PIN_D"; + case NOTIFY_DP_PIN_E: + return "DP_PIN_E"; + case NOTIFY_DP_PIN_F: + return "DP_PIN_F"; + default: + return "UNKNOWN"; + } +} + +static const char *ccic_alternatemode_string(uint64_t id) +{ + if ((id & ALTERNATE_MODE_READY) && (id & ALTERNATE_MODE_START)) + return "READY & START"; + else if ((id & ALTERNATE_MODE_READY) && (id & ALTERNATE_MODE_STOP)) + return "READY & STOP"; + else if (id & ALTERNATE_MODE_READY) + return "MODE READY"; + else if (id & ALTERNATE_MODE_START) + return "START"; + else if (id & ALTERNATE_MODE_STOP) + return "STOP"; + else if (id & ALTERNATE_MODE_RESET) + return "RESET"; + else + return "UNDEFINED"; +} + +static void print_ccic_event(struct seq_file *m, unsigned long long ts, + unsigned long rem_nsec, int cc_type, uint64_t *noti) +{ + struct ccic_type type = *(struct ccic_type *)noti; + int cable = type.sub3; + + switch (cc_type) { + case NOTIFY_FUNCSTATE: + seq_printf(m, "[%5lu.%06lu] function state = %llu\n", + (unsigned long)ts, rem_nsec / 1000, *noti); + break; + case NOTIFY_ALTERNATEMODE: + seq_printf(m, "[%5lu.%06lu] ccic alternate mode is %s 0x%04llx\n", + (unsigned long)ts, rem_nsec / 1000, + ccic_alternatemode_string(*noti), *noti); + break; + case NOTIFY_CCIC_EVENT: + if (type.id == NOTIFY_ID_ATTACH) + seq_printf(m, + "[%5lu.%06lu] ccic notify: id=%s src=%s dest=%s rprd=%s rpstatus=%s cable=%d %s\n", + (unsigned long)ts, rem_nsec / 1000, + ccic_id_string(type.id), + ccic_dev_string(type.src), + ccic_dev_string(type.dest), + ccic_rprd_string(type.sub2), + ccic_rpstatus_string(type.sub3), + cable, ccic_con_string(type.sub1)); + else if (type.id == NOTIFY_ID_RID) + seq_printf(m, "[%5lu.%06lu] ccic notify: id=%s src=%s dest=%s rid=%s\n", + (unsigned long)ts, rem_nsec / 1000, + ccic_id_string(type.id), + ccic_dev_string(type.src), + ccic_dev_string(type.dest), + ccic_rid_string(type.sub1)); + else if (type.id == NOTIFY_ID_USB) + seq_printf(m, "[%5lu.%06lu] ccic notify: id=%s src=%s dest=%s status=%s\n", + (unsigned long)ts, rem_nsec / 1000, + ccic_id_string(type.id), + ccic_dev_string(type.src), + ccic_dev_string(type.dest), + usbstatus_string(type.sub2)); +#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER + else if (type.id == NOTIFY_ID_POWER_STATUS) + seq_printf(m, "[%5lu.%06lu] ccic notify: id=%s src=%s dest=%s %s\n", + (unsigned long)ts, rem_nsec / 1000, + ccic_id_string(type.id), + ccic_dev_string(type.src), + ccic_dev_string(type.dest), + ccic_con_string(type.sub1)); +#endif + else if (type.id == NOTIFY_ID_WATER) + seq_printf(m, "[%5lu.%06lu] ccic notify: id=%s src=%s dest=%s %s detected\n", + (unsigned long)ts, rem_nsec / 1000, + ccic_id_string(type.id), + ccic_dev_string(type.src), + ccic_dev_string(type.dest), + type.sub1 ? "WATER":"DRY"); + else if (type.id == NOTIFY_ID_VCONN) + seq_printf(m, "[%5lu.%06lu] ccic notify: id=%s src=%s dest=%s\n", + (unsigned long)ts, rem_nsec / 1000, + ccic_id_string(type.id), + ccic_dev_string(type.src), + ccic_dev_string(type.dest)); + else if (type.id == NOTIFY_ID_DP_CONNECT) + seq_printf(m, "[%5lu.%06lu] ccic notify: id=%s src=%s dest=%s 0x%04x/0x%04x %s\n", + (unsigned long)ts, rem_nsec / 1000, + ccic_id_string(type.id), + ccic_dev_string(type.src), + ccic_dev_string(type.dest), + type.sub2, + type.sub3, + ccic_con_string(type.sub1)); + else if (type.id == NOTIFY_ID_DP_HPD) + seq_printf(m, "[%5lu.%06lu] ccic notify: id=%s src=%s dest=%s hpd=%s irq=%s\n", + (unsigned long)ts, rem_nsec / 1000, + ccic_id_string(type.id), + ccic_dev_string(type.src), + ccic_dev_string(type.dest), + ccic_hpd_string(type.sub1), + type.sub2 ? "VALID":"NONE"); + else if (type.id == NOTIFY_ID_DP_LINK_CONF) + seq_printf(m, "[%5lu.%06lu] ccic notify: id=%s src=%s dest=%s PIN-assign=%s\n", + (unsigned long)ts, rem_nsec / 1000, + ccic_id_string(type.id), + ccic_dev_string(type.src), + ccic_dev_string(type.dest), + ccic_pinA_string(type.sub1)); + else if (type.id == NOTIFY_ID_USB_DP) + seq_printf(m, "[%5lu.%06lu] ccic notify: id=%s src=%s dest=%s CON=%d HS=%d\n", + (unsigned long)ts, rem_nsec / 1000, + ccic_id_string(type.id), + ccic_dev_string(type.src), + ccic_dev_string(type.dest), + type.sub1, + type.sub2); + else if (type.id == NOTIFY_ID_ROLE_SWAP) + seq_printf(m, "[%5lu.%06lu] ccic notify: id=%s src=%s dest=%s sub1=%d sub2=%d\n", + (unsigned long)ts, rem_nsec / 1000, + ccic_id_string(type.id), + ccic_dev_string(type.src), + ccic_dev_string(type.dest), + type.sub1, + type.sub2); + else + seq_printf(m, "[%5lu.%06lu] ccic notify: id=%s src=%s dest=%s rprd=%s %s\n", + (unsigned long)ts, rem_nsec / 1000, + ccic_id_string(type.id), + ccic_dev_string(type.src), + ccic_dev_string(type.dest), + ccic_rprd_string(type.sub2), + ccic_con_string(type.sub1)); + break; + case NOTIFY_MANAGER: + if (type.id == NOTIFY_ID_ATTACH) + seq_printf(m, + "[%5lu.%06lu] manager notify: id=%s src=%s dest=%s rprd=%s rpstatus=%s cable=%d %s\n", + (unsigned long)ts, rem_nsec / 1000, + ccic_id_string(type.id), + ccic_dev_string(type.src), + ccic_dev_string(type.dest), + ccic_rprd_string(type.sub2), + ccic_rpstatus_string(type.sub3), + cable, ccic_con_string(type.sub1)); + else if (type.id == NOTIFY_ID_RID) + seq_printf(m, "[%5lu.%06lu] manager notify: id=%s src=%s dest=%s rid=%s\n", + (unsigned long)ts, rem_nsec / 1000, + ccic_id_string(type.id), + ccic_dev_string(type.src), + ccic_dev_string(type.dest), + ccic_rid_string(type.sub1)); + else if (type.id == NOTIFY_ID_USB) + seq_printf(m, "[%5lu.%06lu] manager notify: id=%s src=%s dest=%s status=%s\n", + (unsigned long)ts, rem_nsec / 1000, + ccic_id_string(type.id), + ccic_dev_string(type.src), + ccic_dev_string(type.dest), + usbstatus_string(type.sub2)); +#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER + else if (type.id == NOTIFY_ID_POWER_STATUS) + seq_printf(m, "[%5lu.%06lu] manager notify: id=%s src=%s dest=%s %s\n", + (unsigned long)ts, rem_nsec / 1000, + ccic_id_string(type.id), + ccic_dev_string(type.src), + ccic_dev_string(type.dest), + ccic_con_string(type.sub1)); +#endif + else if (type.id == NOTIFY_ID_WATER) + seq_printf(m, "[%5lu.%06lu] manager notify: id=%s src=%s dest=%s %s detected\n", + (unsigned long)ts, rem_nsec / 1000, + ccic_id_string(type.id), + ccic_dev_string(type.src), + ccic_dev_string(type.dest), + type.sub1 ? "WATER":"DRY"); + else if (type.id == NOTIFY_ID_VCONN) + seq_printf(m, "[%5lu.%06lu] manager notify: id=%s src=%s dest=%s\n", + (unsigned long)ts, rem_nsec / 1000, + ccic_id_string(type.id), + ccic_dev_string(type.src), + ccic_dev_string(type.dest)); + else if (type.id == NOTIFY_ID_DP_CONNECT) + seq_printf(m, "[%5lu.%06lu] manager notify: id=%s src=%s dest=%s 0x%04x/0x%04x %s\n", + (unsigned long)ts, rem_nsec / 1000, + ccic_id_string(type.id), + ccic_dev_string(type.src), + ccic_dev_string(type.dest), + type.sub2, + type.sub3, + ccic_con_string(type.sub1)); + else if (type.id == NOTIFY_ID_DP_HPD) + seq_printf(m, "[%5lu.%06lu] manager notify: id=%s src=%s dest=%s hpd=%s irq=%s\n", + (unsigned long)ts, rem_nsec / 1000, + ccic_id_string(type.id), + ccic_dev_string(type.src), + ccic_dev_string(type.dest), + ccic_hpd_string(type.sub1), + type.sub2 ? "VALID":"NONE"); + else if (type.id == NOTIFY_ID_DP_LINK_CONF) + seq_printf(m, "[%5lu.%06lu] manager notify: id=%s src=%s dest=%s PIN-assign=%s\n", + (unsigned long)ts, rem_nsec / 1000, + ccic_id_string(type.id), + ccic_dev_string(type.src), + ccic_dev_string(type.dest), + ccic_pinA_string(type.sub1)); + else if (type.id == NOTIFY_ID_USB_DP) + seq_printf(m, "[%5lu.%06lu] manager notify: id=%s src=%s dest=%s CON=%d HS=%d\n", + (unsigned long)ts, rem_nsec / 1000, + ccic_id_string(type.id), + ccic_dev_string(type.src), + ccic_dev_string(type.dest), + type.sub1, + type.sub2); + else if (type.id == NOTIFY_ID_ROLE_SWAP) + seq_printf(m, "[%5lu.%06lu] manager notify: id=%s src=%s dest=%s syb1=%d sub2=%d\n", + (unsigned long)ts, rem_nsec / 1000, + ccic_id_string(type.id), + ccic_dev_string(type.src), + ccic_dev_string(type.dest), + type.sub1, + type.sub2); + else + seq_printf(m, "[%5lu.%06lu] manager notify: id=%s src=%s dest=%s rprd=%s %s\n", + (unsigned long)ts, rem_nsec / 1000, + ccic_id_string(type.id), + ccic_dev_string(type.src), + ccic_dev_string(type.dest), + ccic_rprd_string(type.sub2), + ccic_con_string(type.sub1)); + break; + default: + pr_info("%s undefined event\n", __func__); + break; + } +} + +static int usblog_proc_show(struct seq_file *m, void *v) +{ + struct usblog_buf *temp_usblog_buffer; + unsigned long long ts; + unsigned long rem_nsec; + unsigned long i; + + temp_usblog_buffer = usblog_root.usblog_buffer; + + if (!temp_usblog_buffer) + goto err; + + seq_printf(m, + "usblog CC IC version:\n"); + + seq_printf(m, + "hw version =%2x %2x %2x %2x\n", + usblog_root.ccic_ver.hw_version[3], + usblog_root.ccic_ver.hw_version[2], + usblog_root.ccic_ver.hw_version[1], + usblog_root.ccic_ver.hw_version[0]); + + seq_printf(m, + "sw version =%2x %2x %2x %2x\n", + usblog_root.ccic_ver.sw_main[2], + usblog_root.ccic_ver.sw_main[1], + usblog_root.ccic_ver.sw_main[0], + usblog_root.ccic_ver.sw_boot); + + seq_printf(m, + "\n\n"); + seq_printf(m, + "usblog CCIC EVENT: count=%llu maxline=%d\n", + temp_usblog_buffer->ccic_count, + USBLOG_CCIC_BUFFER_SIZE); + + if (temp_usblog_buffer->ccic_count >= USBLOG_CCIC_BUFFER_SIZE) { + for (i = temp_usblog_buffer->ccic_index; + i < USBLOG_CCIC_BUFFER_SIZE; i++) { + ts = temp_usblog_buffer->ccic_buffer[i].ts_nsec; + rem_nsec = do_div(ts, 1000000000); + print_ccic_event(m, ts, rem_nsec, + temp_usblog_buffer->ccic_buffer[i].cc_type, + &temp_usblog_buffer->ccic_buffer[i].noti); + } + } + + for (i = 0; i < temp_usblog_buffer->ccic_index; i++) { + ts = temp_usblog_buffer->ccic_buffer[i].ts_nsec; + rem_nsec = do_div(ts, 1000000000); + print_ccic_event(m, ts, rem_nsec, + temp_usblog_buffer->ccic_buffer[i].cc_type, + &temp_usblog_buffer->ccic_buffer[i].noti); + } + + seq_printf(m, + "\n\n"); + seq_printf(m, + "usblog USB_MODE: count=%llu maxline=%d\n", + temp_usblog_buffer->mode_count, + USBLOG_MODE_BUFFER_SIZE); + + if (temp_usblog_buffer->mode_count >= USBLOG_MODE_BUFFER_SIZE) { + for (i = temp_usblog_buffer->mode_index; + i < USBLOG_MODE_BUFFER_SIZE; i++) { + ts = temp_usblog_buffer->mode_buffer[i].ts_nsec; + rem_nsec = do_div(ts, 1000000000); + seq_printf(m, "[%5lu.%06lu] %s\n", (unsigned long)ts, + rem_nsec / 1000, + temp_usblog_buffer->mode_buffer[i].usbmode_str); + } + } + + for (i = 0; i < temp_usblog_buffer->mode_index; i++) { + ts = temp_usblog_buffer->mode_buffer[i].ts_nsec; + rem_nsec = do_div(ts, 1000000000); + seq_printf(m, "[%5lu.%06lu] %s\n", (unsigned long)ts, + rem_nsec / 1000, + temp_usblog_buffer->mode_buffer[i].usbmode_str); + } + seq_printf(m, + "\n\n"); + seq_printf(m, + "usblog USB STATE: count=%llu maxline=%d\n", + temp_usblog_buffer->state_count, + USBLOG_STATE_BUFFER_SIZE); + + if (temp_usblog_buffer->state_count >= USBLOG_STATE_BUFFER_SIZE) { + for (i = temp_usblog_buffer->state_index; + i < USBLOG_STATE_BUFFER_SIZE; i++) { + ts = temp_usblog_buffer->state_buffer[i].ts_nsec; + rem_nsec = do_div(ts, 1000000000); + seq_printf(m, "[%5lu.%06lu] %s\n", (unsigned long)ts, + rem_nsec / 1000, + usbstate_string(temp_usblog_buffer-> + state_buffer[i].usbstate)); + } + } + + for (i = 0; i < temp_usblog_buffer->state_index; i++) { + ts = temp_usblog_buffer->state_buffer[i].ts_nsec; + rem_nsec = do_div(ts, 1000000000); + seq_printf(m, "[%5lu.%06lu] %s\n", (unsigned long)ts, + rem_nsec / 1000, + usbstate_string(temp_usblog_buffer->state_buffer[i].usbstate)); + } + seq_printf(m, + "\n\n"); + seq_printf(m, + "usblog USB EVENT: count=%llu maxline=%d\n", + temp_usblog_buffer->event_count, + USBLOG_EVENT_BUFFER_SIZE); + + if (temp_usblog_buffer->event_count >= USBLOG_EVENT_BUFFER_SIZE) { + for (i = temp_usblog_buffer->event_index; + i < USBLOG_EVENT_BUFFER_SIZE; i++) { + ts = temp_usblog_buffer->event_buffer[i].ts_nsec; + rem_nsec = do_div(ts, 1000000000); + seq_printf(m, "[%5lu.%06lu] %s %s\n", (unsigned long)ts, + rem_nsec / 1000, + event_string(temp_usblog_buffer->event_buffer[i].event), + status_string(temp_usblog_buffer-> + event_buffer[i].enable)); + } + } + + for (i = 0; i < temp_usblog_buffer->event_index; i++) { + ts = temp_usblog_buffer->event_buffer[i].ts_nsec; + rem_nsec = do_div(ts, 1000000000); + seq_printf(m, "[%5lu.%06lu] %s %s\n", (unsigned long)ts, + rem_nsec / 1000, + event_string(temp_usblog_buffer->event_buffer[i].event), + status_string(temp_usblog_buffer->event_buffer[i].enable)); + } +err: + return 0; +} + +static int usblog_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, usblog_proc_show, NULL); +} + +static const struct file_operations usblog_proc_fops = { + .open = usblog_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +void ccic_store_usblog_notify(int type, uint64_t *param1) +{ + struct ccic_buf *ccic_buffer; + unsigned long long *target_count; + unsigned long *target_index; + + target_count = &usblog_root.usblog_buffer->ccic_count; + target_index = &usblog_root.usblog_buffer->ccic_index; + ccic_buffer = &usblog_root.usblog_buffer->ccic_buffer[*target_index]; + if (ccic_buffer == NULL) { + pr_err("%s target_buffer error\n", __func__); + goto err; + } + ccic_buffer->ts_nsec = local_clock(); + ccic_buffer->cc_type = type; + ccic_buffer->noti = *param1; + + *target_index = (*target_index+1)%USBLOG_CCIC_BUFFER_SIZE; + (*target_count)++; +err: + return; +} + +void mode_store_usblog_notify(int type, char *param1) +{ + struct mode_buf *md_buffer; + unsigned long long *target_count; + unsigned long *target_index; + char buf[256], buf2[4]; + char *b, *name; + int param_len; + + target_count = &usblog_root.usblog_buffer->mode_count; + target_index = &usblog_root.usblog_buffer->mode_index; + md_buffer = &usblog_root.usblog_buffer->mode_buffer[*target_index]; + if (md_buffer == NULL) { + pr_err("%s target_buffer error\n", __func__); + goto err; + } + md_buffer->ts_nsec = local_clock(); + + strlcpy(buf, param1, sizeof(buf)); + b = strim(buf); + + if (type == NOTIFY_USBMODE_EXTRA) { + param_len = strlen(b); + if (param_len >= USBLOG_MAX_STRING_SIZE) + param_len = USBLOG_MAX_STRING_SIZE-1; + strncpy(md_buffer->usbmode_str, b, param_len); + } else if (type == NOTIFY_USBMODE) { + if (b) { + name = strsep(&b, ","); + strlcpy(buf2, name, sizeof(buf2)); + strncpy(md_buffer->usbmode_str, buf2, + sizeof(md_buffer->usbmode_str)-1); + } + while (b) { + name = strsep(&b, ","); + if (!name) + continue; + if (USBLOG_MAX_STRING_SIZE + - strlen(md_buffer->usbmode_str) < 5) { + strncpy(md_buffer->usbmode_str, "overflow", + sizeof(md_buffer->usbmode_str)-1); + b = NULL; + } else { + strncat(md_buffer->usbmode_str, ",", 1); + strncat(md_buffer->usbmode_str, name, 3); + } + } + } + + *target_index = (*target_index+1)%USBLOG_MODE_BUFFER_SIZE; + (*target_count)++; +err: + return; +} + +void state_store_usblog_notify(int type, char *param1) +{ + struct state_buf *st_buffer; + unsigned long long *target_count; + unsigned long *target_index; + char buf[256], index, index2, index3; + char *b, *name; + int usbstate; + + target_count = &usblog_root.usblog_buffer->state_count; + target_index = &usblog_root.usblog_buffer->state_index; + st_buffer = &usblog_root.usblog_buffer->state_buffer[*target_index]; + if (st_buffer == NULL) { + pr_err("%s target_buffer error\n", __func__); + goto err; + } + st_buffer->ts_nsec = local_clock(); + + strlcpy(buf, param1, sizeof(buf)); + b = strim(buf); + name = strsep(&b, "="); + + index = *(b+USBLOG_CMP_INDEX); + + switch (index) { + case 'F': /* CONFIGURED */ + usbstate = NOTIFY_CONFIGURED; + break; + case 'N': /* CONNECTED */ + usbstate = NOTIFY_CONNECTED; + break; + case 'C': /* DISCONNECTED */ + usbstate = NOTIFY_DISCONNECTED; + break; + case 'E': /* RESET */ + name = strsep(&b, ":"); + if (b) { + index2 = *b; + switch (index2) { + case 'L': /* FULL SPEED */ + usbstate = NOTIFY_RESET_FULL; + break; + case 'H': /* HIGH SPEED */ + usbstate = NOTIFY_RESET_HIGH; + break; + case 'S': /* SUPER SPEED */ + usbstate = NOTIFY_RESET_SUPER; + break; + default: + usbstate = NOTIFY_RESET; + break; + } + } else + usbstate = NOTIFY_RESET; + break; + case 'L': /* GADGET PULL UP/DN */ + name = strsep(&b, ":"); + if (b) { + index2 = *b; + name = strsep(&b, ":"); + if (b) + index3 = *b; + else /* X means none */ + index3 = 'X'; + } else /* X means none */ + index2 = 'X'; + + switch (index2) { + case 'E': /* VBUS SESSION ENABLE */ + if (index3 == 'S') + usbstate = NOTIFY_PULLUP_EN_SUCCESS; + else if (index3 == 'F') + usbstate = NOTIFY_PULLUP_EN_FAIL; + else + usbstate = NOTIFY_PULLUP_ENABLE; + break; + case 'D': /* VBUS SESSION DISABLE */ + if (index3 == 'S') + usbstate = NOTIFY_PULLUP_DIS_SUCCESS; + else if (index3 == 'F') + usbstate = NOTIFY_PULLUP_DIS_FAIL; + else + usbstate = NOTIFY_PULLUP_DISABLE; + break; + default: + usbstate = NOTIFY_PULLUP; + break; + } + break; + case 'R': /* ACCESSORY START */ + usbstate = NOTIFY_ACCSTART; + break; + case 'S': /* GADGET_VBUS EN/DN*/ + name = strsep(&b, ":"); + if (b) { + index2 = *b; + name = strsep(&b, ":"); + if (b) + index3 = *b; + else /* X means none */ + index3 = 'X'; + } else /* X means none */ + index2 = 'X'; + + switch (index2) { + case 'E': /* VBUS SESSION ENABLE */ + if (index3 == 'S') + usbstate = NOTIFY_VBUS_EN_SUCCESS; + else if (index3 == 'F') + usbstate = NOTIFY_VBUS_EN_FAIL; + else + usbstate = NOTIFY_VBUS_SESSION_ENABLE; + break; + case 'D': /* VBUS SESSION DISABLE */ + if (index3 == 'S') + usbstate = NOTIFY_VBUS_DIS_SUCCESS; + else if (index3 == 'F') + usbstate = NOTIFY_VBUS_DIS_FAIL; + else + usbstate = NOTIFY_VBUS_SESSION_DISABLE; + break; + default: + usbstate = NOTIFY_VBUS_SESSION; + break; + } + break; + case 'D': /* SUPER SPEED */ + usbstate = NOTIFY_SUPER; + break; + case 'H': /*HIGH SPEED */ + usbstate = NOTIFY_HIGH; + break; + default: + pr_err("%s state param error. state=%s\n", __func__, param1); + goto err; + } + + st_buffer->usbstate = usbstate; + + *target_index = (*target_index+1)%USBLOG_STATE_BUFFER_SIZE; + (*target_count)++; +err: + return; +} + +void event_store_usblog_notify(int type, unsigned long *param1, int *param2) +{ + struct event_buf *ev_buffer; + unsigned long long *target_count; + unsigned long *target_index; + + target_count = &usblog_root.usblog_buffer->event_count; + target_index = &usblog_root.usblog_buffer->event_index; + ev_buffer = &usblog_root.usblog_buffer->event_buffer[*target_index]; + if (ev_buffer == NULL) { + pr_err("%s target_buffer error\n", __func__); + goto err; + } + ev_buffer->ts_nsec = local_clock(); + ev_buffer->event = *param1; + ev_buffer->enable = *param2; + + *target_index = (*target_index+1)%USBLOG_EVENT_BUFFER_SIZE; + (*target_count)++; +err: + return; +} + +void store_usblog_notify(int type, void *param1, void *param2) +{ + unsigned long flags = 0; + uint64_t temp = 0; + + if (!usblog_root.init) + register_usblog_proc(); + + spin_lock_irqsave(&usblog_root.usblog_lock, flags); + + if (!usblog_root.usblog_buffer) { + pr_err("%s usblog_buffer is null\n", __func__); + spin_unlock_irqrestore(&usblog_root.usblog_lock, flags); + return; + } + + if (type == NOTIFY_FUNCSTATE || type == NOTIFY_ALTERNATEMODE) { + temp = *(int *)param1; + ccic_store_usblog_notify(type, &temp); + } else if (type == NOTIFY_CCIC_EVENT + || type == NOTIFY_MANAGER) + ccic_store_usblog_notify(type, (uint64_t *)param1); + else if (type == NOTIFY_EVENT) + event_store_usblog_notify(type, + (unsigned long *)param1, (int *)param2); + else if (type == NOTIFY_USBMODE + || type == NOTIFY_USBMODE_EXTRA) + mode_store_usblog_notify(type, (char *)param1); + else if (type == NOTIFY_USBSTATE) + state_store_usblog_notify(type, (char *)param1); + else + pr_err("%s type error %d\n", __func__, type); + + spin_unlock_irqrestore(&usblog_root.usblog_lock, flags); +} +EXPORT_SYMBOL(store_usblog_notify); + +void store_ccic_version(unsigned char *hw, unsigned char *sw_main, + unsigned char *sw_boot) +{ + if (!hw || !sw_main || !sw_boot) { + pr_err("%s null buffer\n", __func__); + return; + } + + memcpy(&usblog_root.ccic_ver.hw_version, hw, 4); + memcpy(&usblog_root.ccic_ver.sw_main, sw_main, 3); + memcpy(&usblog_root.ccic_ver.sw_boot, sw_boot, 1); +} +EXPORT_SYMBOL(store_ccic_version); + +#if defined(CONFIG_USB_HW_PARAM) +unsigned long long show_ccic_version(void) +{ + unsigned long long ret = 0; + + memcpy(&ret, &usblog_root.ccic_ver, sizeof(unsigned long long)); + return ret; +} +EXPORT_SYMBOL(show_ccic_version); +#endif + +int register_usblog_proc(void) +{ + int ret = 0; + struct otg_notify *o_notify = get_otg_notify(); + + if (usblog_root.init) { + pr_err("%s already registered\n", __func__); + if (o_notify != NULL) + goto err; + } + + spin_lock_init(&usblog_root.usblog_lock); + + usblog_root.init = 1; + + proc_create("usblog", 0, NULL, &usblog_proc_fops); + + usblog_root.usblog_buffer + = kzalloc(sizeof(struct usblog_buf), GFP_KERNEL); + if (!usblog_root.usblog_buffer) { + ret = -ENOMEM; + goto err; + } + pr_info("%s size=%zu\n", __func__, sizeof(struct usblog_buf)); +err: + return ret; +} +EXPORT_SYMBOL(register_usblog_proc); + +void unregister_usblog_proc(void) +{ + kfree(usblog_root.usblog_buffer); + usblog_root.usblog_buffer = NULL; + remove_proc_entry("usblog", NULL); + usblog_root.init = 0; +} +EXPORT_SYMBOL(unregister_usblog_proc); + diff --git a/include/linux/battery/sec_charging_common.h b/include/linux/battery/sec_charging_common.h new file mode 100644 index 000000000000..e02ed6bc9e91 --- /dev/null +++ b/include/linux/battery/sec_charging_common.h @@ -0,0 +1,941 @@ +/* + * sec_charging_common.h + * Samsung Mobile Charging Common Header + * + * Copyright (C) 2012 Samsung Electronics, Inc. + * + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#ifndef __SEC_CHARGING_COMMON_H +#define __SEC_CHARGING_COMMON_H __FILE__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* definitions */ +#define SEC_SIZEOF_POWER_SUPPLY_TYPE POWER_SUPPLY_TYPE_MAX + +enum sec_battery_voltage_mode { + /* average voltage */ + SEC_BATTERY_VOLTAGE_AVERAGE = 0, + /* open circuit voltage */ + SEC_BATTERY_VOLTAGE_OCV, +}; + +enum sec_battery_current_mode { + /* uA */ + SEC_BATTERY_CURRENT_UA = 0, + /* mA */ + SEC_BATTERY_CURRENT_MA, +}; + +enum sec_battery_capacity_mode { + /* designed capacity */ + SEC_BATTERY_CAPACITY_DESIGNED = 0, + /* absolute capacity by fuel gauge */ + SEC_BATTERY_CAPACITY_ABSOLUTE, + /* temperary capacity in the time */ + SEC_BATTERY_CAPACITY_TEMPERARY, + /* current capacity now */ + SEC_BATTERY_CAPACITY_CURRENT, + /* cell aging information */ + SEC_BATTERY_CAPACITY_AGEDCELL, + /* charge count */ + SEC_BATTERY_CAPACITY_CYCLE, +}; + +enum sec_wireless_info_mode { + SEC_WIRELESS_OTP_FIRM_RESULT = 0, + SEC_WIRELESS_IC_GRADE, + SEC_WIRELESS_IC_REVISION, + SEC_WIRELESS_OTP_FIRM_VER_BIN, + SEC_WIRELESS_OTP_FIRM_VER, + SEC_WIRELESS_TX_FIRM_RESULT, + SEC_WIRELESS_TX_FIRM_VER, + SEC_TX_FIRMWARE, + SEC_WIRELESS_OTP_FIRM_VERIFY, +}; + +enum sec_wireless_firm_update_mode { + SEC_WIRELESS_RX_SDCARD_MODE = 0, + SEC_WIRELESS_RX_BUILT_IN_MODE, + SEC_WIRELESS_TX_ON_MODE, + SEC_WIRELESS_TX_OFF_MODE, + SEC_WIRELESS_RX_INIT, +}; + +enum sec_tx_firmware_mode { + SEC_TX_OFF = 0, + SEC_TX_STANDBY, + SEC_TX_POWER_TRANSFER, + SEC_TX_ERROR, +}; + +enum sec_wireless_control_mode { + WIRELESS_VOUT_OFF = 0, + WIRELESS_VOUT_NORMAL_VOLTAGE, /* 5V , reserved by factory */ + WIRELESS_VOUT_RESERVED, /* 6V */ + WIRELESS_VOUT_HIGH_VOLTAGE, /* 9V , reserved by factory */ + WIRELESS_VOUT_CC_CV_VOUT, + WIRELESS_VOUT_CV_CALL, + WIRELESS_VOUT_CC_CALL, + WIRELESS_VOUT_5V, + WIRELESS_VOUT_9V, + WIRELESS_VOUT_9V_OTG, + WIRELESS_PAD_FAN_OFF, + WIRELESS_PAD_FAN_ON, + WIRELESS_PAD_LED_OFF, + WIRELESS_PAD_LED_ON, + WIRELESS_VRECT_ADJ_ON, + WIRELESS_VRECT_ADJ_OFF, + WIRELESS_VRECT_ADJ_ROOM_0, + WIRELESS_VRECT_ADJ_ROOM_1, + WIRELESS_VRECT_ADJ_ROOM_2, + WIRELESS_VRECT_ADJ_ROOM_3, + WIRELESS_VRECT_ADJ_ROOM_4, + WIRELESS_VRECT_ADJ_ROOM_5, + WIRELESS_CLAMP_ENABLE, +}; + +enum sec_siop_event_mode { + SIOP_EVENT_IDLE = 0, + SIOP_EVENT_WPC_CALL_START, /* 5V wireless charging + Call */ + SIOP_EVENT_WPC_CALL_END, /* 5V wireless charging + Call */ + SIOP_EVENT_MAX, /* end */ +}; + +enum sec_wireless_pad_mode { + SEC_WIRELESS_PAD_NONE = 0, + SEC_WIRELESS_PAD_WPC, + SEC_WIRELESS_PAD_WPC_HV, + SEC_WIRELESS_PAD_WPC_PACK, + SEC_WIRELESS_PAD_WPC_PACK_TA, + SEC_WIRELESS_PAD_WPC_STAND, + SEC_WIRELESS_PAD_WPC_STAND_HV, + SEC_WIRELESS_PAD_PMA, +}; + +/* ADC type */ +enum sec_battery_adc_type { + /* NOT using this ADC channel */ + SEC_BATTERY_ADC_TYPE_NONE = 0, + /* ADC in AP */ + SEC_BATTERY_ADC_TYPE_AP, + /* ADC by additional IC */ + SEC_BATTERY_ADC_TYPE_IC, + SEC_BATTERY_ADC_TYPE_NUM +}; + +enum sec_battery_adc_channel { + SEC_BAT_ADC_CHANNEL_CABLE_CHECK = 0, + SEC_BAT_ADC_CHANNEL_BAT_CHECK, + SEC_BAT_ADC_CHANNEL_TEMP, + SEC_BAT_ADC_CHANNEL_TEMP_AMBIENT, + SEC_BAT_ADC_CHANNEL_FULL_CHECK, + SEC_BAT_ADC_CHANNEL_VOLTAGE_NOW, + SEC_BAT_ADC_CHANNEL_CHG_TEMP, + SEC_BAT_ADC_CHANNEL_INBAT_VOLTAGE, + SEC_BAT_ADC_CHANNEL_DISCHARGING_CHECK, + SEC_BAT_ADC_CHANNEL_DISCHARGING_NTC, + SEC_BAT_ADC_CHANNEL_WPC_TEMP, + SEC_BAT_ADC_CHANNEL_SLAVE_CHG_TEMP, + SEC_BAT_ADC_CHANNEL_NUM, +}; + +enum sec_battery_charge_mode { + SEC_BAT_CHG_MODE_CHARGING = 0, + SEC_BAT_CHG_MODE_CHARGING_OFF, + SEC_BAT_CHG_MODE_BUCK_OFF, +}; + +/* charging mode */ +enum sec_battery_charging_mode { + /* no charging */ + SEC_BATTERY_CHARGING_NONE = 0, + /* 1st charging */ + SEC_BATTERY_CHARGING_1ST, + /* 2nd charging */ + SEC_BATTERY_CHARGING_2ND, + /* recharging */ + SEC_BATTERY_CHARGING_RECHARGING, +}; + +/* chg_temp state */ +enum sec_battery_chg_temp_state { + SEC_BATTERY_CHG_TEMP_NONE = 0, + SEC_BATTERY_CHG_TEMP_HIGH_1ST, + SEC_BATTERY_CHG_TEMP_HIGH_2ND, +}; + +/* pad_limit state */ +enum sec_battery_wpc_pad_state { + SEC_BATTERY_WPC_TEMP_NONE = 0, + SEC_BATTERY_WPC_TEMP_HIGH, +}; + +/* bat_temp state */ +enum sec_battery_bat_temp_state { + SEC_BATTERY_MIX_TEMP_NONE = 0, + SEC_BATTERY_MIX_TEMP_HIGH, +}; + +/* heat_limit state */ +enum sec_battery_wc_heat_state { + SEC_BATTERY_WC_HEAT_NONE = 0, /* (9V, 1A), (9V, 600mA) */ + SEC_BATTERY_WC_HEAT_HIGH, /* (5V, 400mA) */ +}; +struct sec_bat_adc_api { + bool (*init)(struct platform_device *); + bool (*exit)(void); + int (*read)(unsigned int); +}; +#define sec_bat_adc_api_t struct sec_bat_adc_api + +/* monitor activation */ +enum sec_battery_polling_time_type { + /* same order with power supply status */ + SEC_BATTERY_POLLING_TIME_BASIC = 0, + SEC_BATTERY_POLLING_TIME_CHARGING, + SEC_BATTERY_POLLING_TIME_DISCHARGING, + SEC_BATTERY_POLLING_TIME_NOT_CHARGING, + SEC_BATTERY_POLLING_TIME_SLEEP, +}; + +enum sec_battery_monitor_polling { + /* polling work queue */ + SEC_BATTERY_MONITOR_WORKQUEUE, + /* alarm polling */ + SEC_BATTERY_MONITOR_ALARM, + /* timer polling (NOT USE) */ + SEC_BATTERY_MONITOR_TIMER, +}; +#define sec_battery_monitor_polling_t \ + enum sec_battery_monitor_polling + +/* full charged check : POWER_SUPPLY_PROP_STATUS */ +enum sec_battery_full_charged { + SEC_BATTERY_FULLCHARGED_NONE = 0, + /* current check by ADC */ + SEC_BATTERY_FULLCHARGED_ADC, + /* fuel gauge current check */ + SEC_BATTERY_FULLCHARGED_FG_CURRENT, + /* time check */ + SEC_BATTERY_FULLCHARGED_TIME, + /* SOC check */ + SEC_BATTERY_FULLCHARGED_SOC, + /* charger GPIO, NO additional full condition */ + SEC_BATTERY_FULLCHARGED_CHGGPIO, + /* charger interrupt, NO additional full condition */ + SEC_BATTERY_FULLCHARGED_CHGINT, + /* charger power supply property, NO additional full condition */ + SEC_BATTERY_FULLCHARGED_CHGPSY, +}; + +/* Self discharger type */ +enum sec_battery_discharger_type { + /* type ADC */ + SEC_BAT_SELF_DISCHARGING_BY_ADC = 0, + /* type Fuel Gauge */ + SEC_BAT_SELF_DISCHARGING_BY_FG, +}; + +#define sec_battery_full_charged_t \ + enum sec_battery_full_charged + +/* full check condition type (can be used overlapped) */ +#define sec_battery_full_condition_t unsigned int +/* SEC_BATTERY_FULL_CONDITION_NOTIMEFULL + * full-charged by absolute-timer only in high voltage + */ +#define SEC_BATTERY_FULL_CONDITION_NOTIMEFULL 1 +/* SEC_BATTERY_FULL_CONDITION_NOSLEEPINFULL + * do not set polling time as sleep polling time in full-charged + */ +#define SEC_BATTERY_FULL_CONDITION_NOSLEEPINFULL 2 +/* SEC_BATTERY_FULL_CONDITION_SOC + * use capacity for full-charged check + */ +#define SEC_BATTERY_FULL_CONDITION_SOC 4 +/* SEC_BATTERY_FULL_CONDITION_VCELL + * use VCELL for full-charged check + */ +#define SEC_BATTERY_FULL_CONDITION_VCELL 8 +/* SEC_BATTERY_FULL_CONDITION_AVGVCELL + * use average VCELL for full-charged check + */ +#define SEC_BATTERY_FULL_CONDITION_AVGVCELL 16 +/* SEC_BATTERY_FULL_CONDITION_OCV + * use OCV for full-charged check + */ +#define SEC_BATTERY_FULL_CONDITION_OCV 32 + +/* recharge check condition type (can be used overlapped) */ +#define sec_battery_recharge_condition_t unsigned int +/* SEC_BATTERY_RECHARGE_CONDITION_SOC + * use capacity for recharging check + */ +#define SEC_BATTERY_RECHARGE_CONDITION_SOC 1 +/* SEC_BATTERY_RECHARGE_CONDITION_AVGVCELL + * use average VCELL for recharging check + */ +#define SEC_BATTERY_RECHARGE_CONDITION_AVGVCELL 2 +/* SEC_BATTERY_RECHARGE_CONDITION_VCELL + * use VCELL for recharging check + */ +#define SEC_BATTERY_RECHARGE_CONDITION_VCELL 4 + +/* battery check : POWER_SUPPLY_PROP_PRESENT */ +enum sec_battery_check { + /* No Check for internal battery */ + SEC_BATTERY_CHECK_NONE, + /* by ADC */ + SEC_BATTERY_CHECK_ADC, + /* by callback function (battery certification by 1 wired)*/ + SEC_BATTERY_CHECK_CALLBACK, + /* by PMIC */ + SEC_BATTERY_CHECK_PMIC, + /* by fuel gauge */ + SEC_BATTERY_CHECK_FUELGAUGE, + /* by charger */ + SEC_BATTERY_CHECK_CHARGER, + /* by interrupt (use check_battery_callback() to check battery) */ + SEC_BATTERY_CHECK_INT, +}; +#define sec_battery_check_t \ + enum sec_battery_check + +/* OVP, UVLO check : POWER_SUPPLY_PROP_HEALTH */ +enum sec_battery_ovp_uvlo { + /* by callback function */ + SEC_BATTERY_OVP_UVLO_CALLBACK, + /* by PMIC polling */ + SEC_BATTERY_OVP_UVLO_PMICPOLLING, + /* by PMIC interrupt */ + SEC_BATTERY_OVP_UVLO_PMICINT, + /* by charger polling */ + SEC_BATTERY_OVP_UVLO_CHGPOLLING, + /* by charger interrupt */ + SEC_BATTERY_OVP_UVLO_CHGINT, +}; +#define sec_battery_ovp_uvlo_t \ + enum sec_battery_ovp_uvlo + +/* thermal source */ +enum sec_battery_thermal_source { + /* by fuel gauge */ + SEC_BATTERY_THERMAL_SOURCE_FG, + /* by external source */ + SEC_BATTERY_THERMAL_SOURCE_CALLBACK, + /* by ADC */ + SEC_BATTERY_THERMAL_SOURCE_ADC, +}; +#define sec_battery_thermal_source_t \ + enum sec_battery_thermal_source + +/* temperature check type */ +enum sec_battery_temp_check { + SEC_BATTERY_TEMP_CHECK_NONE = 0, /* no temperature check */ + SEC_BATTERY_TEMP_CHECK_ADC, /* by ADC value */ + SEC_BATTERY_TEMP_CHECK_TEMP, /* by temperature */ +}; +#define sec_battery_temp_check_t \ + enum sec_battery_temp_check + +/* cable check (can be used overlapped) */ +#define sec_battery_cable_check_t unsigned int +/* SEC_BATTERY_CABLE_CHECK_NOUSBCHARGE + * for USB cable in tablet model, + * status is stuck into discharging, + * but internal charging logic is working + */ +#define SEC_BATTERY_CABLE_CHECK_NOUSBCHARGE 1 +/* SEC_BATTERY_CABLE_CHECK_NOINCOMPATIBLECHARGE + * for incompatible charger + * (Not compliant to USB specification, + * cable type is POWER_SUPPLY_TYPE_UNKNOWN), + * do NOT charge and show message to user + * (only for VZW) + */ +#define SEC_BATTERY_CABLE_CHECK_NOINCOMPATIBLECHARGE 2 +/* SEC_BATTERY_CABLE_CHECK_PSY + * check cable by power supply set_property + */ +#define SEC_BATTERY_CABLE_CHECK_PSY 4 +/* SEC_BATTERY_CABLE_CHECK_INT + * check cable by interrupt + */ +#define SEC_BATTERY_CABLE_CHECK_INT 8 +/* SEC_BATTERY_CABLE_CHECK_CHGINT + * check cable by charger interrupt + */ +#define SEC_BATTERY_CABLE_CHECK_CHGINT 16 +/* SEC_BATTERY_CABLE_CHECK_POLLING + * check cable by GPIO polling + */ +#define SEC_BATTERY_CABLE_CHECK_POLLING 32 + +/* check cable source (can be used overlapped) */ +#define sec_battery_cable_source_t unsigned int +/* SEC_BATTERY_CABLE_SOURCE_EXTERNAL + * already given by external argument + */ +#define SEC_BATTERY_CABLE_SOURCE_EXTERNAL 1 +/* SEC_BATTERY_CABLE_SOURCE_CALLBACK + * by callback (MUIC, USB switch) + */ +#define SEC_BATTERY_CABLE_SOURCE_CALLBACK 2 +/* SEC_BATTERY_CABLE_SOURCE_ADC + * by ADC + */ +#define SEC_BATTERY_CABLE_SOURCE_ADC 4 + +/* capacity calculation type (can be used overlapped) */ +#define sec_fuelgauge_capacity_type_t int +/* SEC_FUELGAUGE_CAPACITY_TYPE_RESET + * use capacity information to reset fuel gauge + * (only for driver algorithm, can NOT be set by user) + */ +#define SEC_FUELGAUGE_CAPACITY_TYPE_RESET (-1) +/* SEC_FUELGAUGE_CAPACITY_TYPE_RAW + * use capacity information from fuel gauge directly + */ +#define SEC_FUELGAUGE_CAPACITY_TYPE_RAW 1 +/* SEC_FUELGAUGE_CAPACITY_TYPE_SCALE + * rescale capacity by scaling, need min and max value for scaling + */ +#define SEC_FUELGAUGE_CAPACITY_TYPE_SCALE 2 +/* SEC_FUELGAUGE_CAPACITY_TYPE_DYNAMIC_SCALE + * change only maximum capacity dynamically + * to keep time for every SOC unit + */ +#define SEC_FUELGAUGE_CAPACITY_TYPE_DYNAMIC_SCALE 4 +/* SEC_FUELGAUGE_CAPACITY_TYPE_ATOMIC + * change capacity value by only -1 or +1 + * no sudden change of capacity + */ +#define SEC_FUELGAUGE_CAPACITY_TYPE_ATOMIC 8 +/* SEC_FUELGAUGE_CAPACITY_TYPE_SKIP_ABNORMAL + * skip current capacity value + * if it is abnormal value + */ +#define SEC_FUELGAUGE_CAPACITY_TYPE_SKIP_ABNORMAL 16 + +/* charger function settings (can be used overlapped) */ +#define sec_charger_functions_t unsigned int +/* SEC_CHARGER_NO_GRADUAL_CHARGING_CURRENT + * disable gradual charging current setting + * SUMMIT:AICL, MAXIM:regulation loop + */ +#define SEC_CHARGER_NO_GRADUAL_CHARGING_CURRENT 1 + +/* SEC_CHARGER_MINIMUM_SIOP_CHARGING_CURRENT + * charging current should be over than USB charging current + */ +#define SEC_CHARGER_MINIMUM_SIOP_CHARGING_CURRENT 2 + +/** + * struct sec_bat_adc_table_data - adc to temperature table for sec battery + * driver + * @adc: adc value + * @temperature: temperature(C) * 10 + */ +struct sec_bat_adc_table_data { + int adc; + int data; +}; +#define sec_bat_adc_table_data_t \ + struct sec_bat_adc_table_data + +struct sec_bat_adc_region { + int min; + int max; +}; +#define sec_bat_adc_region_t \ + struct sec_bat_adc_region + +struct sec_charging_current { +#ifdef CONFIG_OF + unsigned int input_current_limit; + unsigned int fast_charging_current; + unsigned int full_check_current_1st; + unsigned int full_check_current_2nd; +#else + int input_current_limit; + int fast_charging_current; + int full_check_current_1st; + int full_check_current_2nd; +#endif +}; + +#define sec_charging_current_t \ + struct sec_charging_current + +#if defined(CONFIG_BATTERY_AGE_FORECAST) +struct sec_age_data { + unsigned int cycle; + unsigned int float_voltage; + unsigned int recharge_condition_vcell; + unsigned int full_condition_vcell; + unsigned int full_condition_soc; +}; + +#define sec_age_data_t \ + struct sec_age_data +#endif + +struct sec_battery_platform_data { + /* NO NEED TO BE CHANGED */ + /* callback functions */ + void (*initial_check)(void); + void (*monitor_additional_check)(void); + bool (*bat_gpio_init)(void); + bool (*fg_gpio_init)(void); + bool (*is_lpm)(void); + bool (*check_jig_status)(void); + bool (*is_interrupt_cable_check_possible)(int); + int (*check_cable_callback)(void); + int (*get_cable_from_extended_cable_type)(int); + bool (*cable_switch_check)(void); + bool (*cable_switch_normal)(void); + bool (*check_cable_result_callback)(int); + bool (*check_battery_callback)(void); + bool (*check_battery_result_callback)(void); + int (*ovp_uvlo_callback)(void); + bool (*ovp_uvlo_result_callback)(int); + bool (*fuelalert_process)(bool); + bool (*get_temperature_callback)( + enum power_supply_property, + union power_supply_propval*); + + /* ADC API for each ADC type */ + sec_bat_adc_api_t adc_api[SEC_BATTERY_ADC_TYPE_NUM]; + /* ADC region by power supply type + * ADC region should be exclusive + */ + sec_bat_adc_region_t *cable_adc_value; + /* charging current for type (0: not use) */ + sec_charging_current_t *charging_current; +#ifdef CONFIG_OF + unsigned int *polling_time; + char *chip_vendor; + unsigned int temp_adc_type; +#else + int *polling_time; +#endif + /* NO NEED TO BE CHANGED */ + unsigned int pre_afc_input_current; + unsigned int pre_wc_afc_input_current; + unsigned int store_mode_afc_input_current; + unsigned int store_mode_hv_wireless_input_current; + + char *pmic_name; + + /* battery */ + char *vendor; + int technology; + int battery_type; + void *battery_data; + + int bat_gpio_ta_nconnected; + /* 1 : active high, 0 : active low */ + int bat_polarity_ta_nconnected; + int bat_irq; + int bat_irq_gpio; + unsigned long bat_irq_attr; + int jig_irq; + unsigned long jig_irq_attr; + sec_battery_cable_check_t cable_check_type; + sec_battery_cable_source_t cable_source_type; + + bool use_LED; /* use charging LED */ + + /* flag for skipping the swelling mode */ + bool swelling_mode_skip_in_high_temp; + /* sustaining event after deactivated (second) */ + unsigned int event_waiting_time; + + /* battery swelling */ + int swelling_high_temp_block; + int swelling_high_temp_recov; + int swelling_low_temp_block; + int swelling_low_temp_recov; + unsigned int swelling_low_temp_current; + unsigned int swelling_low_temp_topoff; + unsigned int swelling_high_temp_current; + unsigned int swelling_high_temp_topoff; + unsigned int swelling_normal_float_voltage; + unsigned int swelling_drop_float_voltage; + unsigned int swelling_high_rechg_voltage; + unsigned int swelling_low_rechg_voltage; + +#if defined(CONFIG_CALC_TIME_TO_FULL) + unsigned int ttf_hv_charge_current; + unsigned int ttf_hv_wireless_charge_current; +#endif + +#if defined(CONFIG_STEP_CHARGING) + /* step charging */ + unsigned int *step_charging_condition; + unsigned int *step_charging_current; +#endif + + /* self discharging */ + bool self_discharging_en; + unsigned int discharging_adc_max; + unsigned int discharging_adc_min; + unsigned int self_discharging_voltage_limit; + unsigned int discharging_ntc_limit; + int force_discharging_limit; + int force_discharging_recov; + int factory_discharging; + unsigned int self_discharging_type; +#if defined(CONFIG_SW_SELF_DISCHARGING) + /* sw self discharging */ + int self_discharging_temp_block; + int self_discharging_volt_block; + int self_discharging_temp_recov; + int self_discharging_temp_pollingtime; +#endif + + /* Monitor setting */ + sec_battery_monitor_polling_t polling_type; + /* for initial check */ + unsigned int monitor_initial_count; + + /* Battery check */ + sec_battery_check_t battery_check_type; + /* how many times do we need to check battery */ + unsigned int check_count; + /* ADC */ + /* battery check ADC maximum value */ + unsigned int check_adc_max; + /* battery check ADC minimum value */ + unsigned int check_adc_min; + + /* OVP/UVLO check */ + sec_battery_ovp_uvlo_t ovp_uvlo_check_type; + + sec_battery_thermal_source_t thermal_source; + + /* + * inbat_adc_table + * in-battery voltage check for table models: + * To read real battery voltage with Jig cable attached, + * dedicated hw pin & conversion table of adc-voltage are required + */ +#ifdef CONFIG_OF + sec_bat_adc_table_data_t *temp_adc_table; + sec_bat_adc_table_data_t *temp_amb_adc_table; + sec_bat_adc_table_data_t *chg_temp_adc_table; + sec_bat_adc_table_data_t *wpc_temp_adc_table; + sec_bat_adc_table_data_t *slave_chg_temp_adc_table; + sec_bat_adc_table_data_t *inbat_adc_table; +#else + const sec_bat_adc_table_data_t *temp_adc_table; + const sec_bat_adc_table_data_t *temp_amb_adc_table; +#endif + unsigned int temp_adc_table_size; + unsigned int temp_amb_adc_table_size; + unsigned int chg_temp_adc_table_size; + unsigned int wpc_temp_adc_table_size; + unsigned int slave_chg_temp_adc_table_size; + unsigned int inbat_adc_table_size; + + sec_battery_temp_check_t temp_check_type; + unsigned int temp_check_count; + unsigned int chg_temp_check; /* Control the charging current depending on the chg_thm */ + unsigned int chg_thermal_source; /* To confirm the charger temperature */ + unsigned int wpc_temp_check; + unsigned int mix_temp_check; + unsigned int wpc_thermal_source; /* To confirm the wpc temperature */ + unsigned int slave_chg_temp_check; + unsigned int slave_thermal_source; /* To confirm the slave charger temperature */ + unsigned int inbat_voltage; + + /* + * limit can be ADC value or Temperature + * depending on temp_check_type + * temperature should be temp x 10 (0.1 degree) + */ + int temp_highlimit_threshold_normal; + int temp_highlimit_recovery_normal; + int temp_high_threshold_normal; + int temp_high_recovery_normal; + int temp_low_threshold_normal; + int temp_low_recovery_normal; + int temp_highlimit_threshold_lpm; + int temp_highlimit_recovery_lpm; + int temp_high_threshold_lpm; + int temp_high_recovery_lpm; + int temp_low_threshold_lpm; + int temp_low_recovery_lpm; + int chg_high_temp_1st; + int chg_high_temp_2nd; + int chg_high_temp_recovery; + unsigned int chg_charging_limit_current; + unsigned int chg_charging_limit_current_2nd; + unsigned int chg_skip_check_time; + unsigned int chg_skip_check_capacity; + int wpc_high_temp; + int wpc_high_temp_recovery; + int wpc_heat_temp_recovery; + int wpc_lcd_on_high_temp; + int wpc_lcd_on_high_temp_rec; + unsigned int wpc_hv_lcd_on_input_limit_current; + unsigned int wpc_charging_limit_current; + unsigned int sleep_mode_limit_current; + unsigned int wc_full_input_limit_current; + unsigned int wc_heating_input_limit_current; + unsigned int wc_heating_time; + unsigned int wc_cv_current; + unsigned int wpc_skip_check_time; + unsigned int wpc_skip_check_capacity; + int mix_high_tbat; + int mix_high_tchg; + int mix_high_tbat_recov; + unsigned int mix_input_limit_current; + int mix_high_tbat_hv; + int mix_high_tchg_hv; + int mix_high_tbat_recov_hv; + unsigned int mix_input_limit_current_hv; + + /* If these is NOT full check type or NONE full check type, + * it is skipped + */ + /* 1st full check */ + sec_battery_full_charged_t full_check_type; + /* 2nd full check */ + sec_battery_full_charged_t full_check_type_2nd; + unsigned int full_check_count; + int chg_gpio_full_check; + /* 1 : active high, 0 : active low */ + int chg_polarity_full_check; + sec_battery_full_condition_t full_condition_type; + unsigned int full_condition_soc; + unsigned int full_condition_vcell; + unsigned int full_condition_avgvcell; + unsigned int full_condition_ocv; + + unsigned int recharge_check_count; + sec_battery_recharge_condition_t recharge_condition_type; + unsigned int recharge_condition_soc; + unsigned int recharge_condition_avgvcell; + unsigned int recharge_condition_vcell; + + /* for absolute timer (second) */ + unsigned long charging_total_time; + /* for recharging timer (second) */ + unsigned long recharging_total_time; + /* reset charging for abnormal malfunction (0: not use) */ + unsigned long charging_reset_time; + + /* fuel gauge */ + char *fuelgauge_name; + int fg_irq; + unsigned long fg_irq_attr; + /* fuel alert SOC (-1: not use) */ + int fuel_alert_soc; + /* fuel alert can be repeated */ + bool repeated_fuelalert; + sec_fuelgauge_capacity_type_t capacity_calculation_type; + /* soc should be soc x 10 (0.1% degree) + * only for scaling + */ + int capacity_max; + int capacity_max_hv; + + int capacity_max_margin; + int capacity_min; + + /* charger */ + char *charger_name; + char *fgsrc_switch_name; + bool support_fgsrc_change; + + /* wireless charger */ + char *wireless_charger_name; + int wireless_cc_cv; + int wpc_det; + int wpc_en; + + int chg_gpio_en; + int chg_irq; + unsigned long chg_irq_attr; + /* float voltage (mV) */ +#ifdef CONFIG_OF + unsigned int chg_float_voltage; +#else + int chg_float_voltage; +#endif +#if defined(CONFIG_BATTERY_AGE_FORECAST) + int num_age_step; + int age_step; + int age_data_length; + sec_age_data_t* age_data; +#endif + unsigned int siop_event_check_type; + unsigned int siop_call_cc_current; + unsigned int siop_call_cv_current; + + int siop_input_limit_current; + int siop_charging_limit_current; + int siop_hv_input_limit_current; + int siop_hv_charging_limit_current; + int siop_wireless_input_limit_current; + int siop_wireless_charging_limit_current; + int siop_hv_wireless_input_limit_current; + int siop_hv_wireless_charging_limit_current; + + sec_charger_functions_t chg_functions_setting; + + bool fake_capacity; + + /* ADC setting */ + unsigned int adc_check_count; + /* ADC type for each channel */ + unsigned int adc_type[]; +}; + +struct sec_charger_platform_data { + bool (*chg_gpio_init)(void); + + /* charging current for type (0: not use) */ + sec_charging_current_t *charging_current; + + /* wirelss charger */ + char *wireless_charger_name; + int wireless_cc_cv; + + int vbus_ctrl_gpio; + int chg_gpio_en; + /* float voltage (mV) */ + int chg_float_voltage; + int irq_gpio; + int chg_irq; + unsigned long chg_irq_attr; + + /* otg_en setting */ + int otg_en; + + /* OVP/UVLO check */ + sec_battery_ovp_uvlo_t ovp_uvlo_check_type; + /* 1st full check */ + sec_battery_full_charged_t full_check_type; + /* 2nd full check */ + sec_battery_full_charged_t full_check_type_2nd; + + sec_charger_functions_t chg_functions_setting; +}; + +struct sec_fuelgauge_platform_data { + bool (*fg_gpio_init)(void); + bool (*check_jig_status)(void); + int (*check_cable_callback)(void); + bool (*fuelalert_process)(bool); + + /* charging current for type (0: not use) */ + sec_charging_current_t *charging_current; + + int jig_irq; + int jig_gpio; + unsigned long jig_irq_attr; + + sec_battery_thermal_source_t thermal_source; + + int fg_irq; + unsigned long fg_irq_attr; + /* fuel alert SOC (-1: not use) */ + int fuel_alert_soc; + int fuel_alert_vol; + /* fuel alert can be repeated */ + bool repeated_fuelalert; + sec_fuelgauge_capacity_type_t capacity_calculation_type; + /* soc should be soc x 10 (0.1% degree) + * only for scaling + */ + int capacity_max; + int capacity_max_hv; + int capacity_max_margin; + int capacity_min; + +#if defined(CONFIG_BATTERY_AGE_FORECAST) + unsigned int full_condition_soc; +#endif +}; + +#define sec_battery_platform_data_t \ + struct sec_battery_platform_data + +#define sec_charger_platform_data_t \ + struct sec_charger_platform_data + +#define sec_fuelgauge_platform_data_t \ + struct sec_fuelgauge_platform_data + +static inline struct power_supply *get_power_supply_by_name(char *name) +{ + if (!name) + return (struct power_supply *)NULL; + else + return power_supply_get_by_name(name); +} + +#define psy_do_property(name, function, property, value) \ +{ \ + struct power_supply *psy; \ + int ret; \ + psy = get_power_supply_by_name((name)); \ + if (!psy) { \ + pr_err("%s: Fail to "#function" psy (%s)\n", \ + __func__, (name)); \ + value.intval = 0; \ + } else { \ + if (psy->desc->function##_property != NULL) { \ + ret = psy->desc->function##_property(psy, (property), &(value)); \ + if (ret < 0) { \ + pr_err("%s: Fail to %s "#function" (%d=>%d)\n", \ + __func__, name, (property), ret); \ + value.intval = 0; \ + } \ + } \ + } \ +} + +#ifndef CONFIG_OF +#define adc_init(pdev, pdata, channel) \ + (((pdata)->adc_api)[((((pdata)->adc_type[(channel)]) < \ + SEC_BATTERY_ADC_TYPE_NUM) ? ((pdata)->adc_type[(channel)]) : \ + SEC_BATTERY_ADC_TYPE_NONE)].init((pdev))) + +#define adc_exit(pdata, channel) \ + (((pdata)->adc_api)[((pdata)->adc_type[(channel)])].exit()) + +#define adc_read(pdata, channel) \ + (((pdata)->adc_api)[((pdata)->adc_type[(channel)])].read((channel))) +#endif + +#define get_battery_data(driver) \ + (((struct battery_data_t *)(driver)->pdata->battery_data) \ + [(driver)->pdata->battery_type]) + +#endif /* __SEC_CHARGING_COMMON_H */ diff --git a/include/linux/external_notify.h b/include/linux/external_notify.h new file mode 100644 index 000000000000..ce8c5da0256a --- /dev/null +++ b/include/linux/external_notify.h @@ -0,0 +1,71 @@ +/* + * include/linux/external_notify.h + * + * header file supporting usb notify layer + * external notify call chain information + * + * Copyright (C) 2016-2017 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 + * + */ + + /* usb notify layer v3.1 */ + +#ifndef __EXTERNAL_NOTIFY_H__ +#define __EXTERNAL_NOTIFY_H__ + +#include + +/* external notifier call chain command */ +enum external_notify_cmd { + EXTERNAL_NOTIFY_3S_NODEVICE = 1, + EXTERNAL_NOTIFY_DEVICE_CONNECT, + EXTERNAL_NOTIFY_HOSTBLOCK_PRE, + EXTERNAL_NOTIFY_HOSTBLOCK_POST, + EXTERNAL_NOTIFY_MDMBLOCK_PRE, + EXTERNAL_NOTIFY_MDMBLOCK_POST, + EXTERNAL_NOTIFY_POWERROLE, +}; + +/* external notifier call sequence, + * largest priority number device will be called first. */ +enum external_notify_device { + EXTERNAL_NOTIFY_DEV_MUIC, + EXTERNAL_NOTIFY_DEV_CHARGER, + EXTERNAL_NOTIFY_DEV_PDIC, +}; + +enum external_notify_condev { + EXTERNAL_NOTIFY_NONE = 0, + EXTERNAL_NOTIFY_GPAD, + EXTERNAL_NOTIFY_LANHUB, +}; + +#ifdef CONFIG_USB_EXTERNAL_NOTIFY +extern int send_external_notify(unsigned long cmd, int data); +extern int usb_external_notify_register(struct notifier_block *nb, + notifier_fn_t notifier, int listener); +extern int usb_external_notify_unregister(struct notifier_block *nb); +#else +static inline int send_external_notify(unsigned long cmd, + int data) {return 0; } +static inline int usb_external_notify_register(struct notifier_block *nb, + notifier_fn_t notifier, int listener) {return 0; } +static inline int usb_external_notify_unregister(struct notifier_block *nb) + {return 0; } +#endif + +#endif /* __EXTERNAL_NOTIFY_H__ */ diff --git a/include/linux/host_notify.h b/include/linux/host_notify.h new file mode 100644 index 000000000000..1dba7033ffb7 --- /dev/null +++ b/include/linux/host_notify.h @@ -0,0 +1,64 @@ +/* + * Host notify class driver + * + * Copyright (C) 2011-2017 Samsung, Inc. + * Author: Dongrak Shin + * +*/ + + /* usb notify layer v3.1 */ + +#ifndef __LINUX_HOST_NOTIFY_H__ +#define __LINUX_HOST_NOTIFY_H__ + +enum host_uevent_state { + NOTIFY_HOST_NONE, + NOTIFY_HOST_ADD, + NOTIFY_HOST_REMOVE, + NOTIFY_HOST_OVERCURRENT, + NOTIFY_HOST_LOWBATT, + NOTIFY_HOST_BLOCK, + NOTIFY_HOST_UNKNOWN, +}; + +enum otg_hostnotify_mode { + NOTIFY_NONE_MODE, + NOTIFY_HOST_MODE, + NOTIFY_PERIPHERAL_MODE, + NOTIFY_TEST_MODE, +}; + +enum booster_power { + NOTIFY_POWER_OFF, + NOTIFY_POWER_ON, +}; + +enum set_command { + NOTIFY_SET_OFF, + NOTIFY_SET_ON, +}; + +struct host_notify_dev { + const char *name; + struct device *dev; + int index; + int state; + int mode; + int booster; + int (*set_mode)(bool); + int (*set_booster)(bool); +}; + +#ifdef CONFIG_USB_HOST_NOTIFY +extern int host_state_notify(struct host_notify_dev *ndev, int state); +extern int host_notify_dev_register(struct host_notify_dev *ndev); +extern void host_notify_dev_unregister(struct host_notify_dev *ndev); +#else +static inline int host_state_notify(struct host_notify_dev *ndev, int state) + {return 0; } +static inline int host_notify_dev_register(struct host_notify_dev *ndev) + {return 0; } +static inline void host_notify_dev_unregister(struct host_notify_dev *ndev) {} +#endif + +#endif /* __LINUX_HOST_NOTIFY_H__ */ diff --git a/include/linux/mfd/samsung/s2mps18-private.h b/include/linux/mfd/samsung/s2mps18-private.h new file mode 100644 index 000000000000..3bbad8ba1869 --- /dev/null +++ b/include/linux/mfd/samsung/s2mps18-private.h @@ -0,0 +1,482 @@ +/* + * s2mps18-private.h - Voltage regulator driver for the s2mps18 + * + * Copyright (C) 2015 Samsung Electrnoics + * + * 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 + */ + +#ifndef __LINUX_MFD_S2MPS18_PRIV_H +#define __LINUX_MFD_S2MPS18_PRIV_H + +#include +#define S2MPS18_REG_INVALID (0xff) +#define S2MPS18_IRQSRC_PMIC (1 << 0) + +/* PMIC Top-Level Registers */ +#define S2MPS18_PMIC_REG_PMICID 0x00 +#define S2MPS18_PMIC_REG_INTSRC 0x01 +#define S2MPS18_PMIC_REG_INTSRC_MASK 0x02 + +/* Slave addr = 0xCC */ +/* PMIC Registers */ +#define S2MPS18_PMIC_REG_INT1 0x00 +#define S2MPS18_PMIC_REG_INT2 0x01 +#define S2MPS18_PMIC_REG_INT3 0x02 +#define S2MPS18_PMIC_REG_INT4 0x03 +#define S2MPS18_PMIC_REG_INT5 0x04 +#define S2MPS18_PMIC_REG_INT6 0x05 +#define S2MPS18_PMIC_REG_INT7 0x06 +#define S2MPS18_PMIC_REG_INT1M 0x07 +#define S2MPS18_PMIC_REG_INT2M 0x08 +#define S2MPS18_PMIC_REG_INT3M 0x09 +#define S2MPS18_PMIC_REG_INT4M 0x0A +#define S2MPS18_PMIC_REG_INT5M 0x0B +#define S2MPS18_PMIC_REG_INT6M 0x0C +#define S2MPS18_PMIC_REG_INT7M 0x0D +#define S2MPS18_PMIC_REG_STATUS1 0x0E +#define S2MPS18_PMIC_REG_STATUS2 0x0F +#define S2MPS18_PMIC_REG_PWRONSRC 0x10 +#define S2MPS18_PMIC_REG_OFFSRC 0x11 + +#define S2MPS18_PMIC_REG_BUCHG 0x12 +#define S2MPS18_PMIC_REG_RTCBUF 0x13 +#define S2MPS18_PMIC_REG_CTRL1 0x14 +#define S2MPS18_PMIC_REG_CTRL2 0x15 +#define S2MPS18_PMIC_REG_CTRL3 0x16 + +#define S2MPS18_PMIC_REG_B1CTRL 0x1C +#define S2MPS18_PMIC_REG_B1OUT1 0x1D +#define S2MPS18_PMIC_REG_B1OUT2 0x1E +#define S2MPS18_PMIC_REG_B2CTRL 0x1F +#define S2MPS18_PMIC_REG_B2OUT1 0x20 +#define S2MPS18_PMIC_REG_B2OUT2 0x21 +#define S2MPS18_PMIC_REG_B3CTRL 0x22 +#define S2MPS18_PMIC_REG_B3OUT1 0x23 +#define S2MPS18_PMIC_REG_B3OUT2 0x24 +#define S2MPS18_PMIC_REG_B4CTRL 0x25 +#define S2MPS18_PMIC_REG_B4OUT1 0x26 +#define S2MPS18_PMIC_REG_B4OUT2 0x27 +#define S2MPS18_PMIC_REG_B5CTRL 0x28 +#define S2MPS18_PMIC_REG_B5OUT 0x29 +#define S2MPS18_PMIC_REG_B6CTRL 0x2A +#define S2MPS18_PMIC_REG_B6OUT 0x2B +#define S2MPS18_PMIC_REG_B7CTRL 0x2C +#define S2MPS18_PMIC_REG_B7OUT 0x2D +#define S2MPS18_PMIC_REG_B8CTRL 0x2E +#define S2MPS18_PMIC_REG_B8OUT 0x2F +#define S2MPS18_PMIC_REG_B9CTRL 0x30 +#define S2MPS18_PMIC_REG_B9OUT 0x31 +#define S2MPS18_PMIC_REG_B10CTRL 0x32 +#define S2MPS18_PMIC_REG_B10OUT 0x33 +#define S2MPS18_PMIC_REG_B11CTRL 0x34 +#define S2MPS18_PMIC_REG_B11OUT1 0x35 +#define S2MPS18_PMIC_REG_B11OUT2 0x36 +#define S2MPS18_PMIC_REG_B12CTRL 0x37 +#define S2MPS18_PMIC_REG_B12OUT 0x38 +#define S2MPS18_PMIC_REG_B13CTRL 0x39 +#define S2MPS18_PMIC_REG_B13OUT 0x3A +#define S2MPS18_PMIC_REG_B14CTRL 0x3B +#define S2MPS18_PMIC_REG_B14OUT 0x3C +#define S2MPS18_PMIC_REG_BB1CTRL 0x3D +#define S2MPS18_PMIC_REG_BB1OUT 0x3E + +#define S2MPS18_PMIC_REG_BUCKRAMP 0x3F +#define S2MPS18_PMIC_REG_BUCKRAMP2 0x40 +#define S2MPS18_PMIC_REG_BUCK2_PH_DVS 0x41 +#define S2MPS18_PMIC_REG_BUCK5_PH_DVS 0x42 +#define S2MPS18_PMIC_REG_BUCK6_PH_DVS 0x43 +#define S2MPS18_PMIC_REG_LDO6_DVS 0x44 +#define S2MPS18_PMIC_REG_LDO7_DVS 0x45 +#define S2MPS18_PMIC_REG_LDO11_DVS 0x46 +#define S2MPS18_PMIC_REG_LDO29_DVS 0x47 +#define S2MPS18_PMIC_REG_BUCK7_DVS 0x48 + +#define S2MPS18_PMIC_REG_L1CTRL 0x49 +#define S2MPS18_PMIC_REG_L2CTRL1 0x4A +#define S2MPS18_PMIC_REG_L2CTRL2 0x4B +#define S2MPS18_PMIC_REG_L3CTRL 0x4C +#define S2MPS18_PMIC_REG_L4CTRL 0x4D +#define S2MPS18_PMIC_REG_L5CTRL 0x4E +#define S2MPS18_PMIC_REG_L6CTRL 0x4F +#define S2MPS18_PMIC_REG_L7CTRL 0x50 +#define S2MPS18_PMIC_REG_L8CTRL 0x51 +#define S2MPS18_PMIC_REG_L9CTRL 0x52 +#define S2MPS18_PMIC_REG_L10CTRL 0x53 +#define S2MPS18_PMIC_REG_L11CTRL 0x54 +#define S2MPS18_PMIC_REG_L12CTRL 0x55 +#define S2MPS18_PMIC_REG_L13CTRL 0x56 +#define S2MPS18_PMIC_REG_L14CTRL 0x57 +#define S2MPS18_PMIC_REG_L15CTRL 0x58 +#define S2MPS18_PMIC_REG_L16CTRL 0x59 +#define S2MPS18_PMIC_REG_L17CTRL 0x5A +#define S2MPS18_PMIC_REG_L18CTRL 0x5B +#define S2MPS18_PMIC_REG_L19CTRL 0x5C +#define S2MPS18_PMIC_REG_L20CTRL 0x5D +#define S2MPS18_PMIC_REG_L21CTRL 0x5E +#define S2MPS18_PMIC_REG_L22CTRL 0x5F +#define S2MPS18_PMIC_REG_L23CTRL 0x60 +#define S2MPS18_PMIC_REG_L24CTRL 0x61 +#define S2MPS18_PMIC_REG_L25CTRL 0x62 +#define S2MPS18_PMIC_REG_L26CTRL 0x63 +#define S2MPS18_PMIC_REG_L27CTRL 0x64 +#define S2MPS18_PMIC_REG_L28CTRL 0x65 +#define S2MPS18_PMIC_REG_L29CTRL 0x66 +#define S2MPS18_PMIC_REG_L30CTRL 0x67 +#define S2MPS18_PMIC_REG_L31CTRL 0x68 +#define S2MPS18_PMIC_REG_L32CTRL 0x69 +#define S2MPS18_PMIC_REG_L33CTRL 0x6A +#define S2MPS18_PMIC_REG_L34CTRL 0x6B +#define S2MPS18_PMIC_REG_L35CTRL 0x6C +#define S2MPS18_PMIC_REG_L36CTRL 0x6D +#define S2MPS18_PMIC_REG_L37CTRL 0x6E +#define S2MPS18_PMIC_REG_L38CTRL 0x6F +#define S2MPS18_PMIC_REG_L39CTRL 0x70 +#define S2MPS18_PMIC_REG_L40CTRL 0x71 +#define S2MPS18_PMIC_REG_L41CTRL 0x72 +#define S2MPS18_PMIC_REG_L42CTRL 0x73 +#define S2MPS18_PMIC_REG_L43CTRL 0x74 +#define S2MPS18_PMIC_REG_L44CTRL 0x75 +#define S2MPS18_PMIC_REG_L45CTRL 0x76 + +#define S2MPS18_PMIC_REG_LDO_DSCH1 0x78 +#define S2MPS18_PMIC_REG_LDO_DSCH2 0x79 +#define S2MPS18_PMIC_REG_LDO_DSCH3 0x7A +#define S2MPS18_PMIC_REG_LDO_DSCH4 0x7B +#define S2MPS18_PMIC_REG_LDO_DSCH5 0x7C +#define S2MPS18_PMIC_REG_LDO_DSCH6 0x7D +#define S2MPS18_PMIC_REG_LDO_DSCH7 0x7E +#define S2MPS18_PMIC_REG_TCXO_CTRL 0x88 +#define S2MPS18_PMIC_REG_PWREN_SEL1 0xC8 +#define S2MPS18_PMIC_REG_PWREN_SEL2 0xC9 +#define S2MPS18_PMIC_REG_LPM_MODE0 0xCC +#define S2MPS18_PMIC_REG_LPM_MODE1 0xCD +#define S2MPS18_PMIC_REG_LPM_MODE2 0xCE +#define S2MPS18_REG_ADC_CTRL1 0xCF +#define S2MPS18_REG_ADC_CTRL2 0xD0 +#define S2MPS18_REG_ADC_CTRL3 0xD1 +#define S2MPS18_REG_ADC_DATA 0xD2 +#define S2MPS18_PMIC_REG_OCP_WARN1 0xD3 +#define S2MPS18_PMIC_REG_OCP_WARN2 0xD4 + +/* S2MPS18regulator ids */ +enum S2MPS18_regulators { + S2MPS18_LDO1, + S2MPS18_LDO2, + S2MPS18_LDO3, + S2MPS18_LDO4, + S2MPS18_LDO5, + S2MPS18_LDO6, + S2MPS18_LDO7, + S2MPS18_LDO8, + S2MPS18_LDO9, + S2MPS18_LDO10, + S2MPS18_LDO11, + S2MPS18_LDO12, + S2MPS18_LDO13, + S2MPS18_LDO14, +/* S2MPS18_LDO15, + S2MPS18_LDO16, + S2MPS18_LDO17, + S2MPS18_LDO18, + S2MPS18_LDO19, + S2MPS18_LDO20, + S2MPS18_LDO21, +*/ S2MPS18_LDO22, +/* S2MPS18_LDO23, + S2MPS18_LDO24, + S2MPS18_LDO25, + S2MPS18_LDO26, + S2MPS18_LDO27, + S2MPS18_LDO28, + S2MPS18_LDO29, +*/ S2MPS18_LDO30, + S2MPS18_LDO31, + S2MPS18_LDO32, + S2MPS18_LDO33, + S2MPS18_LDO34, + S2MPS18_LDO35, + S2MPS18_LDO36, + S2MPS18_LDO37, + S2MPS18_LDO38, + S2MPS18_LDO39, + S2MPS18_LDO40, + S2MPS18_LDO41, + S2MPS18_LDO42, + S2MPS18_LDO43, + S2MPS18_LDO44, + S2MPS18_LDO45, + S2MPS18_BUCK1, + S2MPS18_BUCK2, + S2MPS18_BUCK3, + S2MPS18_BUCK4, + S2MPS18_BUCK5, + S2MPS18_BUCK6, + S2MPS18_BUCK7, + S2MPS18_BUCK8, + S2MPS18_BUCK9, + S2MPS18_BUCK10, + S2MPS18_BUCK11, + S2MPS18_BUCK12, + S2MPS18_BUCK13, +/* S2MPS18_BUCK14, + S2MPS18_BB1, */ + S2MPS18_REG_MAX, +}; + +#define S2MPS18_BUCK_MIN1 300000 +#define S2MPS18_BUCK_MIN2 600000 +#define S2MPS18_LDO_MIN1 700000 +#define S2MPS18_LDO_MIN2 400000 +#define S2MPS18_LDO_MIN3 1800000 +#define S2MPS18_LDO_MIN4 300000 +#define S2MPS18_BUCK_STEP1 6250 +#define S2MPS18_BUCK_STEP2 12500 +#define S2MPS18_LDO_STEP1 12500 +#define S2MPS18_LDO_STEP2 25000 +#define S2MPS18_LDO_VSEL_MASK 0x3F +#define S2MPS18_BUCK_VSEL_MASK 0xFF +#define S2MPS18_ENABLE_MASK (0x03 << S2MPS18_ENABLE_SHIFT) +#define S2MPS18_SW_ENABLE_MASK 0x03 +#define S2MPS18_RAMP_DELAY 12000 + +#define S2MPS18_ENABLE_TIME_LDO 128 +#define S2MPS18_ENABLE_TIME_BUCK1 130 +#define S2MPS18_ENABLE_TIME_BUCK2 130 +#define S2MPS18_ENABLE_TIME_BUCK3 130 +#define S2MPS18_ENABLE_TIME_BUCK4 130 +#define S2MPS18_ENABLE_TIME_BUCK5 130 +#define S2MPS18_ENABLE_TIME_BUCK6 130 +#define S2MPS18_ENABLE_TIME_BUCK7 130 +#define S2MPS18_ENABLE_TIME_BUCK8 130 +#define S2MPS18_ENABLE_TIME_BUCK9 130 +#define S2MPS18_ENABLE_TIME_BUCK10 130 +#define S2MPS18_ENABLE_TIME_BUCK11 130 +#define S2MPS18_ENABLE_TIME_BUCK12 130 +#define S2MPS18_ENABLE_TIME_BUCK13 130 +#define S2MPS18_ENABLE_TIME_BUCK14 130 +#define S2MPS18_ENABLE_TIME_BB 160 + +#define S2MPS18_ENABLE_SHIFT 0x06 +#define S2MPS18_LDO_N_VOLTAGES (S2MPS18_LDO_VSEL_MASK + 1) +#define S2MPS18_BUCK_N_VOLTAGES (S2MPS18_BUCK_VSEL_MASK + 1) + +#define S2MPS18_PMIC_EN_SHIFT 6 +#define S2MPS18_REGULATOR_MAX (S2MPS18_REG_MAX) +#define SEC_PMIC_REV(iodev) (iodev)->pmic_rev + +#define S2MPS18_OCP_WARN_EN_SHIFT 7 +#define S2MPS18_OCP_WARN_RESET_SHIFT 6 +#define S2MPS18_OCP_WARN_LEVEL_SHIFT 0 + +#define CURRENT_BS 15625 +#define CURRENT_BD 31250 +#define CURRENT_BT 46875 +#define CURRENT_BB 15625 +#define CURRENT_BV 15625 +#define CURRENT_L150 879 +#define CURRENT_L300 1758 +#define CURRENT_L450 2637 +#define CURRENT_L600 3516 +#define CURRENT_L800 4688 + +#define POWER_CMS 977 +#define POWER_CMD 1953 +#define POWER_CMT 2930 +#define POWER_VM 1953 +#define POWER_BB 3906 +#define POWER_N150HL 110 +#define POWER_N300H 220 +#define POWER_N450H 330 +#define POWER_N600HL 439 +#define POWER_P150HL 220 +#define POWER_P300HL 439 +#define POWER_P450HL 659 +#define POWER_P800L 1172 +#define POWER_N150DVS 220 +#define POWER_N300DVS 439 +#define POWER_N450DVS 659 + +#define HIGH_ACC_CURRENT_BS 977 +#define HIGH_ACC_CURRENT_BD 1953 +#define HIGH_ACC_CURRENT_BT 2930 +#define HIGH_ACC_CURRENT_BB 977 +#define HIGH_ACC_CURRENT_BV 977 +#define HIGH_ACC_CURRENT_L150 55 +#define HIGH_ACC_CURRENT_L300 110 +#define HIGH_ACC_CURRENT_L450 165 +#define HIGH_ACC_CURRENT_L600 220 +#define HIGH_ACC_CURRENT_L800 293 + +#define ADC_EN_MASK 0x80 +#define ADC_ASYNCRD_MASK 0x80 +#define ADC_PTR_MASK 0x0F +#define ADC_PGEN_MASK 0x30 +#define CURRENT_MODE 0x00 +#define CHANNELSET_MODE 0x20 +#define POWER_MODE 0x10 +#define RAWCURRENT_MODE 0x30 + +#define S2MPS18_LDO_START 0x41 +#define S2MPS18_LDO_END 0x6D +#define S2MPS18_BUCK_START 0x01 +#define S2MPS18_BUCK_END 0x0E + +#define SMPNUM_MASK 0x0F + +#define SEC_PMIC_REV(iodev) (iodev)->pmic_rev +#define S2MPS18_MAX_ADC_CHANNEL 8 +/* + * sec_opmode_data - regulator operation mode data + * @id: regulator id + * @mode: regulator operation mode + */ + +enum s2mps18_irq_source { + PMIC_INT1 = 0, + PMIC_INT2, + PMIC_INT3, + PMIC_INT4, + PMIC_INT5, + PMIC_INT6, + PMIC_INT7, + + S2MPS18_IRQ_GROUP_NR, +}; + +#define S2MPS18_NUM_IRQ_PMIC_REGS 7 + +enum s2mps18_irq { + /* PMIC */ + S2MPS18_PMIC_IRQ_PWRONR_INT1, + S2MPS18_PMIC_IRQ_PWRONF_INT1, + S2MPS18_PMIC_IRQ_JIGONBF_INT1, + S2MPS18_PMIC_IRQ_JIGONBR_INT1, + S2MPS18_PMIC_IRQ_ACOKBF_INT1, + S2MPS18_PMIC_IRQ_ACOKBR_INT1, + S2MPS18_PMIC_IRQ_PWRON1S_INT1, + S2MPS18_PMIC_IRQ_MRB_INT1, + + S2MPS18_PMIC_IRQ_RTC60S_INT2, + S2MPS18_PMIC_IRQ_RTCA1_INT2, + S2MPS18_PMIC_IRQ_RTCA0_INT2, + S2MPS18_PMIC_IRQ_SMPL_INT2, + S2MPS18_PMIC_IRQ_RTC1S_INT2, + S2MPS18_PMIC_IRQ_WTSR_INT2, + S2MPS18_PMIC_IRQ_WRSTB_INT2, + + S2MPS18_PMIC_IRQ_120C_INT3, + S2MPS18_PMIC_IRQ_140C_INT3, + S2MPS18_PMIC_IRQ_TSD_INT3, + S2MPS18_PMIC_IRQ_ADCDONE_INT3, + + S2MPS18_PMIC_IRQ_OC0_INT4, + S2MPS18_PMIC_IRQ_OC1_INT4, + S2MPS18_PMIC_IRQ_OC2_INT4, + S2MPS18_PMIC_IRQ_OC3_INT4, + S2MPS18_PMIC_IRQ_OC4_INT4, + S2MPS18_PMIC_IRQ_OC5_INT4, + S2MPS18_PMIC_IRQ_OC6_INT4, + S2MPS18_PMIC_IRQ_OC7_INT4, + + S2MPS18_PMIC_IRQ_OCPB1_INT5, + S2MPS18_PMIC_IRQ_OCPB2_INT5, + S2MPS18_PMIC_IRQ_OCPB3_INT5, + S2MPS18_PMIC_IRQ_OCPB4_INT5, + S2MPS18_PMIC_IRQ_OCPB5_INT5, + S2MPS18_PMIC_IRQ_OCPB6_INT5, + S2MPS18_PMIC_IRQ_OCPB7_INT5, + S2MPS18_PMIC_IRQ_OCPB8_INT5, + + S2MPS18_PMIC_IRQ_OCPB9_INT6, + S2MPS18_PMIC_IRQ_OCPB10_INT6, + S2MPS18_PMIC_IRQ_OCPB11_INT6, + S2MPS18_PMIC_IRQ_OCPB12_INT6, + S2MPS18_PMIC_IRQ_OCPB13_INT6, + S2MPS18_PMIC_IRQ_OCPB14_INT6, + S2MPS18_PMIC_IRQ_OCPBB_INT6, + + S2MPS18_PMIC_IRQ_SCLDO20_INT7, + S2MPS18_PMIC_IRQ_SCLDO21_INT7, + S2MPS18_PMIC_IRQ_SCLDO31_INT7, + S2MPS18_PMIC_IRQ_SCLDO44_INT7, + S2MPS18_PMIC_IRQ_SCLDO2_INT7, + S2MPS18_PMIC_IRQ_MASKSCLDOINT_INT7, + + S2MPS18_IRQ_NR, +}; + + +enum sec_device_type { + S2MPS18X, +}; + +struct s2mps18_dev { + struct device *dev; + struct i2c_client *i2c; + struct i2c_client *pmic; + struct i2c_client *rtc; + struct i2c_client *debug_i2c; + struct mutex i2c_lock; + struct apm_ops *ops; + + int type; + int device_type; + int irq; + int irq_base; + int irq_gpio; + bool wakeup; + struct mutex irqlock; + int irq_masks_cur[S2MPS18_IRQ_GROUP_NR]; + int irq_masks_cache[S2MPS18_IRQ_GROUP_NR]; + + /* pmic VER/REV register */ + u8 pmic_rev; /* pmic Rev */ + int adc_mode; + int adc_sync_mode; + + struct s2mps18_platform_data *pdata; +}; + +enum s2mps18_types { + TYPE_S2MPS18, +}; + +extern int s2mps18_irq_init(struct s2mps18_dev *s2mps18); +extern void s2mps18_irq_exit(struct s2mps18_dev *s2mps18); + +extern void s2mps18_powermeter_init(struct s2mps18_dev *s2mps18); +extern void s2mps18_powermeter_deinit(struct s2mps18_dev *s2mps18); + +/* S2MPS18 shared i2c API function */ +extern int s2mps18_read_reg(struct i2c_client *i2c, u8 reg, u8 *dest); +extern int s2mps18_bulk_read(struct i2c_client *i2c, u8 reg, int count, + u8 *buf); +extern int s2mps18_write_reg(struct i2c_client *i2c, u8 reg, u8 value); +extern int s2mps18_bulk_write(struct i2c_client *i2c, u8 reg, int count, + u8 *buf); +extern int s2mps18_write_word(struct i2c_client *i2c, u8 reg, u16 value); +extern int s2mps18_read_word(struct i2c_client *i2c, u8 reg); + +extern int s2mps18_update_reg(struct i2c_client *i2c, u8 reg, u8 val, u8 mask); + +extern int s2m_ldo_set_mode(int ldo_num, unsigned int mode); + +#ifdef CONFIG_EXYNOS_OCP +extern void get_s2mps18_i2c(struct i2c_client **i2c); +#endif + +#endif /* __LINUX_MFD_S2MPS18_PRIV_H */ + diff --git a/include/linux/usb/manager/usb_typec_manager_notifier.h b/include/linux/usb/manager/usb_typec_manager_notifier.h new file mode 100644 index 000000000000..4e6d49727206 --- /dev/null +++ b/include/linux/usb/manager/usb_typec_manager_notifier.h @@ -0,0 +1,174 @@ +/* + * include/linux/muic/ccic_notifier.h + * + * header file supporting CCIC notifier call chain information + * + * Copyright (C) 2010 Samsung Electronics + * Seung-Jin Hahn + * + * 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 + * + */ + +#ifndef __USB_TYPEC_MANAGER_NOTIFIER_H__ +#define __USB_TYPEC_MANAGER_NOTIFIER_H__ + +#include +#include +#include +#ifdef CONFIG_VBUS_NOTIFIER +#include +#endif + + +/* USB TypeC Manager notifier call sequence, + * largest priority number device will be called first. */ + + +/* CCIC notifier call sequence, + * largest priority number device will be called first. */ +typedef enum { +/* MUIC */ + MANAGER_NOTIFY_MUIC_NONE = 0, + MANAGER_NOTIFY_MUIC_DOCK, + MANAGER_NOTIFY_MUIC_MHL, + MANAGER_NOTIFY_MUIC_USB, + MANAGER_NOTIFY_MUIC_TSP, + MANAGER_NOTIFY_MUIC_CHARGER, + MANAGER_NOTIFY_MUIC_CPUIDLE, + MANAGER_NOTIFY_MUIC_CPUFREQ, + MANAGER_NOTIFY_MUIC_TIMEOUT_OPEN_DEVICE, + +/* CCIC */ + MANAGER_NOTIFY_CCIC_INITIAL = 20, + MANAGER_NOTIFY_CCIC_MUIC, + MANAGER_NOTIFY_CCIC_USB, + MANAGER_NOTIFY_CCIC_BATTERY, + MANAGER_NOTIFY_CCIC_DP, + MANAGER_NOTIFY_CCIC_USBDP, + MANAGER_NOTIFY_CCIC_SENSORHUB, + +/* VBUS */ + MANAGER_NOTIFY_VBUS_USB = 30, + MANAGER_NOTIFY_VBUS_CHARGER, +} manager_notifier_device_t; + +typedef enum { + CCIC_NOTY, + MUIC_NOTY, + VBUS_NOTY, + USB_STATE_NOTY, +} manager_notify_t; + +typedef enum { + PD_USB_TYPE, + PD_TA_TYPE, +} pd_usb_state_t; + +#if defined(CONFIG_VBUS_NOTIFIER) +typedef enum { + EVENT_LOAD = 0, + EVENT_CANCEL, +} muic_fake_event; +#endif + +typedef struct +{ + uint64_t src:4; + uint64_t dest:4; + uint64_t id:8; + uint64_t sub1:16; + uint64_t sub2:16; + uint64_t sub3:16; + void *pd; +} MANAGER_NOTI_TYPEDEF; + +typedef struct _manager_data_t +{ + struct blocking_notifier_head manager_muic_notifier; + struct blocking_notifier_head manager_ccic_notifier; + struct notifier_block ccic_nb; + struct notifier_block muic_nb; +// struct notifier_block usb_nb; +// struct notifier_block batter_nb; +#if defined(CONFIG_VBUS_NOTIFIER) + struct notifier_block vbus_nb; +#endif + + struct delayed_work manager_init_work; +// struct workqueue_struct *typec_manager_wq; + struct delayed_work cable_check_work; + struct delayed_work muic_noti_work; + struct delayed_work rtctime_update_work; +#if defined(CONFIG_VBUS_NOTIFIER) + struct delayed_work vbus_noti_work; +#endif + + int muic_action; + int muic_cable_type; + int muic_data_refresh; + int muic_attach_state_without_ccic; +#if defined(CONFIG_VBUS_NOTIFIER) + int muic_fake_event_wq_processing; +#endif + int vbus_state; + + int ccic_attach_state; // USB_STATUS_NOTIFY_DETACH, UFP, DFP, DRP, NO_USB + int ccic_drp_state; + int ccic_rid_state; + int cable_type; + int usb_enum_state; + bool usb_enable_state; + int pd_con_state; + int water_det; + int wVbus_det; + int is_UFPS; + void *pd; + int water_count; + int dry_count; + int usb210_count; + int usb310_count; + int waterChg_count; + unsigned long waterDet_duration; + unsigned long waterDet_time; + unsigned long dryDet_time; + + unsigned long wVbus_duration; + unsigned long wVbusHigh_time; + unsigned long wVbusLow_time; + + int dp_attach_state; + int dp_cable_type; + int dp_hpd_state; + int dp_is_connect; + int dp_hs_connect; + int dp_check_done; +}manager_data_t; + + +#define MANAGER_NOTIFIER_BLOCK(name) \ + struct notifier_block (name) + +extern void manager_notifier_usbdp_support(void); +extern void manager_notifier_test(void *); + + +/* ccic notifier register/unregister API + * for used any where want to receive ccic attached device attach/detach. */ +extern int manager_notifier_register(struct notifier_block *nb, + notifier_fn_t notifier, manager_notifier_device_t listener); +extern int manager_notifier_unregister(struct notifier_block *nb); + +#endif /* __USB_TYPEC_MANAGER_NOTIFIER_H__ */ diff --git a/include/linux/usb_hw_param.h b/include/linux/usb_hw_param.h new file mode 100644 index 000000000000..29b9e5d0a2b7 --- /dev/null +++ b/include/linux/usb_hw_param.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2017 Samsung Electronics Co. Ltd. + * + * 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. + */ + +/* usb hw param */ +/* usb notify layer v3.1 */ + +#define MAX_HWPARAM_STR_LEN 1024 +#define MAX_HWPARAM_STRING 10 +#define HWPARAM_DATA_LIMIT 100000 + +enum usb_hw_param { + USB_CCIC_WATER_INT_COUNT, + USB_CCIC_DRY_INT_COUNT, + USB_CCIC_I2C_ERROR_COUNT, + USB_CCIC_OVC_COUNT, + USB_CCIC_OTG_USE_COUNT, + USB_CCIC_DP_USE_COUNT, + USB_CCIC_VR_USE_COUNT, + USB_HOST_SUPER_SPEED_COUNT, + USB_HOST_HIGH_SPEED_COUNT, + USB_HOST_FULL_SPEED_COUNT, + USB_HOST_LOW_SPEED_COUNT, + USB_CLIENT_SUPER_SPEED_COUNT, + USB_CLIENT_HIGH_SPEED_COUNT, + USB_HOST_CLASS_AUDIO_COUNT, + USB_HOST_CLASS_COMM_COUNT, + USB_HOST_CLASS_HID_COUNT, + USB_HOST_CLASS_PHYSICAL_COUNT, + USB_HOST_CLASS_IMAGE_COUNT, + USB_HOST_CLASS_PRINTER_COUNT, + USB_HOST_CLASS_STORAGE_COUNT, + USB_HOST_CLASS_HUB_COUNT, + USB_HOST_CLASS_CDC_COUNT, + USB_HOST_CLASS_CSCID_COUNT, + USB_HOST_CLASS_CONTENT_COUNT, + USB_HOST_CLASS_VIDEO_COUNT, + USB_HOST_CLASS_WIRELESS_COUNT, + USB_HOST_CLASS_MISC_COUNT, + USB_HOST_CLASS_APP_COUNT, + USB_HOST_CLASS_VENDOR_COUNT, + USB_CCIC_DEX_USE_COUNT, + USB_CCIC_WATER_TIME_DURATION, + USB_CCIC_WATER_VBUS_COUNT, + USB_CCIC_WATER_VBUS_TIME_DURATION, + USB_CCIC_VBUS_CC_SHORT_COUNT, + USB_MUIC_AFC_NACK_COUNT, + USB_MUIC_AFC_ERROR_COUNT, + USB_MUIC_DCD_TIMEOUT_COUNT, + USB_CCIC_VERSION, + USB_CCIC_HW_PARAM_MAX, +}; + +#if defined(CONFIG_USB_TYPEC_MANAGER_NOTIFIER) +int get_ccic_water_count(void); +int get_ccic_dry_count(void); +int get_usb310_count(void); +int get_usb210_count(void); +unsigned long get_waterDet_duration(void); +unsigned long get_wVbus_duration(void); +int get_waterChg_count(void); +unsigned long long show_ccic_version(void); +#endif diff --git a/include/linux/usb_notify.h b/include/linux/usb_notify.h new file mode 100644 index 000000000000..443beffb895a --- /dev/null +++ b/include/linux/usb_notify.h @@ -0,0 +1,211 @@ +/* + * usb notify header + * + * Copyright (C) 2011-2017 Samsung, Inc. + * Author: Dongrak Shin + * +*/ + + /* usb notify layer v3.1 */ + +#ifndef __LINUX_USB_NOTIFY_H__ +#define __LINUX_USB_NOTIFY_H__ + +#include +#include +#include +#include +#if defined(CONFIG_USB_HW_PARAM) +#include +#endif +#include + +enum otg_notify_events { + NOTIFY_EVENT_NONE, + NOTIFY_EVENT_VBUS, + NOTIFY_EVENT_HOST, + NOTIFY_EVENT_CHARGER, + NOTIFY_EVENT_SMARTDOCK_TA, + NOTIFY_EVENT_SMARTDOCK_USB, + NOTIFY_EVENT_AUDIODOCK, + NOTIFY_EVENT_LANHUB, + NOTIFY_EVENT_LANHUB_TA, + NOTIFY_EVENT_MMDOCK, + NOTIFY_EVENT_HMT, + NOTIFY_EVENT_GAMEPAD, + NOTIFY_EVENT_DRIVE_VBUS, + NOTIFY_EVENT_ALL_DISABLE, + NOTIFY_EVENT_HOST_DISABLE, + NOTIFY_EVENT_CLIENT_DISABLE, + NOTIFY_EVENT_MDM_ON_OFF, + NOTIFY_EVENT_OVERCURRENT, + NOTIFY_EVENT_SMSC_OVC, + NOTIFY_EVENT_SMTD_EXT_CURRENT, + NOTIFY_EVENT_MMD_EXT_CURRENT, + NOTIFY_EVENT_DEVICE_CONNECT, + NOTIFY_EVENT_GAMEPAD_CONNECT, + NOTIFY_EVENT_LANHUB_CONNECT, + NOTIFY_EVENT_POWER_SOURCE, + NOTIFY_EVENT_VBUSPOWER, + NOTIFY_EVENT_POGO, + NOTIFY_EVENT_VIRTUAL, +}; + +#define VIRT_EVENT(a) (a+NOTIFY_EVENT_VIRTUAL) +#define PHY_EVENT(a) (a%NOTIFY_EVENT_VIRTUAL) +#define IS_VIRTUAL(a) (a >= NOTIFY_EVENT_VIRTUAL ? 1 : 0) + +enum otg_notify_event_status { + NOTIFY_EVENT_DISABLED, + NOTIFY_EVENT_DISABLING, + NOTIFY_EVENT_ENABLED, + NOTIFY_EVENT_ENABLING, + NOTIFY_EVENT_BLOCKED, + NOTIFY_EVENT_BLOCKING, +}; + +enum otg_notify_evt_type { + NOTIFY_EVENT_EXTRA = (1 << 0), + NOTIFY_EVENT_STATE = (1 << 1), + NOTIFY_EVENT_DELAY = (1 << 2), + NOTIFY_EVENT_NEED_VBUSDRIVE = (1 << 3), + NOTIFY_EVENT_NOBLOCKING = (1 << 4), + NOTIFY_EVENT_NOSAVE = (1 << 5), + NOTIFY_EVENT_NEED_HOST = (1 << 6), + NOTIFY_EVENT_NEED_CLIENT = (1 << 7), +}; + +enum otg_notify_block_type { + NOTIFY_BLOCK_TYPE_NONE = 0, + NOTIFY_BLOCK_TYPE_HOST = (1 << 0), + NOTIFY_BLOCK_TYPE_CLIENT = (1 << 1), + NOTIFY_BLOCK_TYPE_ALL = (1 << 0 | 1 << 1), +}; + +enum otg_notify_mdm_type { + NOTIFY_MDM_TYPE_OFF, + NOTIFY_MDM_TYPE_ON, +}; + +enum otg_notify_gpio { + NOTIFY_VBUS, + NOTIFY_REDRIVER, +}; + +enum otg_op_pos { + NOTIFY_OP_OFF, + NOTIFY_OP_POST, + NOTIFY_OP_PRE, +}; + +enum ovc_check_value { + HNOTIFY_LOW, + HNOTIFY_HIGH, + HNOTIFY_INITIAL, +}; + +enum otg_notify_power_role { + HNOTIFY_SINK, + HNOTIFY_SOURCE, +}; + +enum otg_notify_data_role { + HNOTIFY_UFP, + HNOTIFY_DFP, +}; + +struct otg_notify { + int vbus_detect_gpio; + int redriver_en_gpio; + int is_wakelock; + int unsupport_host; + int smsc_ovc_poll_sec; + int auto_drive_vbus; + int booting_delay_sec; + int disable_control; + int device_check_sec; + int pre_peri_delay_us; + int sec_whitelist_enable; + int speed; + const char *muic_name; + int (*pre_gpio)(int gpio, int use); + int (*post_gpio)(int gpio, int use); + int (*vbus_drive)(bool); + int (*set_host)(bool); + int (*set_peripheral)(bool); + int (*set_charger)(bool); + int (*post_vbus_detect)(bool); + int (*set_lanhubta)(int); + int (*set_battcall)(int, int); + int (*set_chg_current)(int); + void (*set_ldo_onoff)(void *, unsigned int); + void *o_data; + void *u_notify; +}; + +struct otg_booster { + char *name; + int (*booster)(bool); +}; + +#ifdef CONFIG_USB_NOTIFY_LAYER +extern const char *event_string(enum otg_notify_events event); +extern const char *status_string(enum otg_notify_event_status status); +extern void send_otg_notify(struct otg_notify *n, + unsigned long event, int enable); +extern struct otg_booster *find_get_booster(struct otg_notify *n); +extern int register_booster(struct otg_notify *n, struct otg_booster *b); +extern int register_ovc_func(struct otg_notify *n, + int (*check_state)(void *), void *data); +extern int get_usb_mode(struct otg_notify *n); +extern unsigned long get_cable_type(struct otg_notify *n); +extern int is_usb_host(struct otg_notify *n); +extern void *get_notify_data(struct otg_notify *n); +extern void set_notify_data(struct otg_notify *n, void *data); +extern struct otg_notify *get_otg_notify(void); +extern int set_otg_notify(struct otg_notify *n); +extern void put_otg_notify(struct otg_notify *n); +extern bool is_blocked(struct otg_notify *n, int type); +extern int usb_check_whitelist_for_mdm(struct usb_device *dev); +#if defined(CONFIG_USB_HW_PARAM) +extern unsigned long long *get_hw_param(struct otg_notify *n, + enum usb_hw_param index); +extern int inc_hw_param(struct otg_notify *n, + enum usb_hw_param index); +extern int inc_hw_param_host(struct host_notify_dev *dev, + enum usb_hw_param index); +#endif +#else +static inline const char *event_string(enum otg_notify_events event) + {return NULL; } +static inline const char *status_string(enum otg_notify_event_status status) + {return NULL; } +static inline void send_otg_notify(struct otg_notify *n, + unsigned long event, int enable) { } +static inline struct otg_booster *find_get_booster(struct otg_notify *n) + {return NULL; } +static inline int register_booster(struct otg_notify *n, + struct otg_booster *b) {return 0; } +static inline int register_ovc_func(struct otg_notify *n, + int (*check_state)(void *), void *data) {return 0; } +static inline int get_usb_mode(struct otg_notify *n) {return 0; } +static inline unsigned long get_cable_type(struct otg_notify *n) {return 0; } +static inline int is_usb_host(struct otg_notify *n) {return 0; } +static inline void *get_notify_data(struct otg_notify *n) {return NULL; } +static inline void set_notify_data(struct otg_notify *n, void *data) {} +static inline struct otg_notify *get_otg_notify(void) {return NULL; } +static inline int set_otg_notify(struct otg_notify *n) {return 0; } +static inline void put_otg_notify(struct otg_notify *n) {} +static inline bool is_blocked(struct otg_notify *n, int type) {return false; } +static inline int usb_check_whitelist_for_mdm(struct usb_device *dev) + {return 0; } +#if defined(CONFIG_USB_HW_PARAM) +static unsigned long long *get_hw_param(struct otg_notify *n, + enum usb_hw_param index) {return NULL; } +static int inc_hw_param(struct otg_notify *n, + enum usb_hw_param index) {return 0; } +static int inc_hw_param_host(struct host_notify_dev *dev, + enum usb_hw_param index) {return 0; } +#endif +#endif +#endif /* __LINUX_USB_NOTIFY_H__ */ diff --git a/include/linux/usblog_proc_notify.h b/include/linux/usblog_proc_notify.h new file mode 100644 index 000000000000..4840ea18c662 --- /dev/null +++ b/include/linux/usblog_proc_notify.h @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2016-2017 Samsung Electronics Co. Ltd. + * + * 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. + */ + + /* usb notify layer v3.1 */ + +#ifndef __LINUX_USBLOG_PROC_NOTIFY_H__ +#define __LINUX_USBLOG_PROC_NOTIFY_H__ + +enum usblog_type { + NOTIFY_FUNCSTATE, + NOTIFY_ALTERNATEMODE, + NOTIFY_CCIC_EVENT, + NOTIFY_MANAGER, + NOTIFY_USBMODE, + NOTIFY_USBMODE_EXTRA, + NOTIFY_USBSTATE, + NOTIFY_EVENT, +}; + +enum usblog_state { + NOTIFY_CONFIGURED = 1, + NOTIFY_CONNECTED, + NOTIFY_DISCONNECTED, + NOTIFY_RESET, + NOTIFY_RESET_FULL, + NOTIFY_RESET_HIGH, + NOTIFY_RESET_SUPER, + NOTIFY_ACCSTART, + NOTIFY_PULLUP, + NOTIFY_PULLUP_ENABLE, + NOTIFY_PULLUP_EN_SUCCESS, + NOTIFY_PULLUP_EN_FAIL, + NOTIFY_PULLUP_DISABLE, + NOTIFY_PULLUP_DIS_SUCCESS, + NOTIFY_PULLUP_DIS_FAIL, + NOTIFY_VBUS_SESSION, + NOTIFY_VBUS_SESSION_ENABLE, + NOTIFY_VBUS_EN_SUCCESS, + NOTIFY_VBUS_EN_FAIL, + NOTIFY_VBUS_SESSION_DISABLE, + NOTIFY_VBUS_DIS_SUCCESS, + NOTIFY_VBUS_DIS_FAIL, + NOTIFY_HIGH, + NOTIFY_SUPER, +}; + +enum usblog_status { + NOTIFY_DETACH = 0, + NOTIFY_ATTACH_DFP, + NOTIFY_ATTACH_UFP, + NOTIFY_ATTACH_DRP, +}; + +enum ccic_device { + NOTIFY_DEV_INITIAL = 0, + NOTIFY_DEV_USB, + NOTIFY_DEV_BATTERY, + NOTIFY_DEV_PDIC, + NOTIFY_DEV_MUIC, + NOTIFY_DEV_CCIC, +#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER + NOTIFY_DEV_MANAGER, +#endif + NOTIFY_DEV_DP, + NOTIFY_DEV_USB_DP, +}; + +enum ccic_id { + NOTIFY_ID_INITIAL = 0, + NOTIFY_ID_ATTACH, + NOTIFY_ID_RID, + NOTIFY_ID_USB, +#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER + NOTIFY_ID_POWER_STATUS, +#endif + NOTIFY_ID_WATER, + NOTIFY_ID_VCONN, + NOTIFY_ID_DP_CONNECT, + NOTIFY_ID_DP_HPD, + NOTIFY_ID_DP_LINK_CONF, + NOTIFY_ID_USB_DP, + NOTIFY_ID_ROLE_SWAP, +}; + +enum ccic_rid { + NOTIFY_RID_UNDEFINED = 0, + NOTIFY_RID_000K, + NOTIFY_RID_001K, + NOTIFY_RID_255K, + NOTIFY_RID_301K, + NOTIFY_RID_523K, + NOTIFY_RID_619K, + NOTIFY_RID_OPEN, +}; + +enum ccic_con { + NOTIFY_CON_DETACH = 0, + NOTIFY_CON_ATTACH, +}; + +enum ccic_rprd { + NOTIFY_RD = 0, + NOTIFY_RP, +}; + +enum ccic_rpstatus { + NOTIFY_RP_NONE = 0, + NOTIFY_RP_56K, /* 80uA */ + NOTIFY_RP_22K, /* 180uA */ + NOTIFY_RP_10K, /* 330uA */ + NOTIFY_RP_ABNORMAL, +}; + +enum ccic_hpd { + NOTIFY_HPD_LOW = 0, + NOTIFY_HPD_HIGH, + NOTIFY_HPD_IRQ, +}; + +enum ccic_pin_assignment { + NOTIFY_DP_PIN_UNKNOWN = 0, + NOTIFY_DP_PIN_A, + NOTIFY_DP_PIN_B, + NOTIFY_DP_PIN_C, + NOTIFY_DP_PIN_D, + NOTIFY_DP_PIN_E, + NOTIFY_DP_PIN_F, +}; + +#define ALTERNATE_MODE_NOT_READY (1 << 0) +#define ALTERNATE_MODE_READY (1 << 1) +#define ALTERNATE_MODE_STOP (1 << 2) +#define ALTERNATE_MODE_START (1 << 3) +#define ALTERNATE_MODE_RESET (1 << 4) + +#ifdef CONFIG_USB_NOTIFY_PROC_LOG +extern void store_usblog_notify(int type, void *param1, void *parma2); +extern void store_ccic_version(unsigned char *hw, unsigned char *sw_main, + unsigned char *sw_boot); +extern int register_usblog_proc(void); +extern void unregister_usblog_proc(void); +#else +static inline void store_usblog_notify(int type, void *param1, void *parma2) {} +static inline void store_ccic_version(unsigned char *hw, unsigned char *sw_main, + unsigned char *sw_boot) {} +static inline int register_usblog_proc(void) + {return 0; } +static inline void unregister_usblog_proc(void) {} +#endif +#endif +