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"
obj-$(CONFIG_USBIP_CORE) += usbip/
obj-$(CONFIG_TYPEC) += typec/
+obj-$(CONFIG_USB_NOTIFY_LAYER) += notify/
+obj-$(CONFIG_USB_TYPEC_MANAGER_NOTIFIER) += manager/
--- /dev/null
+#
+# 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
--- /dev/null
+#
+# 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
--- /dev/null
+#include <linux/device.h>
+#include <linux/module.h>
+
+#include <linux/notifier.h>
+#include <linux/usb/manager/usb_typec_manager_notifier.h>
+
+#include <linux/ccic/ccic_core.h>
+#include <linux/ccic/ccic_notifier.h>
+
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/workqueue.h>
+#include <linux/sec_sysfs.h>
+
+#if defined(CONFIG_VBUS_NOTIFIER)
+#include <linux/vbus_notifier.h>
+#endif
+#include <linux/usb_notify.h>
+#include <linux/delay.h>
+#include <linux/ccic/s2mm005_ext.h>
+#include <linux/time.h>
+#include <linux/ktime.h>
+#include <linux/rtc.h>
+#include <linux/ccic/max77705_usbc.h>
+#include <linux/ccic/max77705_alternate.h>
+
+#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<CCIC_NOTI_DEST_NUM)?
+ CCIC_NOTI_DEST_Print[manager_noti.src]:"unknown",
+ (manager_noti.dest<CCIC_NOTI_DEST_NUM)?
+ CCIC_NOTI_DEST_Print[manager_noti.dest]:"unknown",
+ (manager_noti.id<CCIC_NOTI_ID_NUM)?
+ CCIC_NOTI_ID_Print[manager_noti.id]:"unknown",
+ manager_noti.sub1, manager_noti.sub2, manager_noti.sub3);
+
+ if (manager_noti.dest == CCIC_NOTIFY_DEV_DP) {
+ if (manager_noti.id == CCIC_NOTIFY_ID_DP_CONNECT) {
+ typec_manager.dp_attach_state = manager_noti.sub1;
+ typec_manager.dp_is_connect = 0;
+ typec_manager.dp_hs_connect = 0;
+ } else if (manager_noti.id == CCIC_NOTIFY_ID_DP_HPD) {
+ typec_manager.dp_hpd_state = manager_noti.sub1;
+ } else if (manager_noti.id == CCIC_NOTIFY_ID_DP_LINK_CONF) {
+ typec_manager.dp_cable_type = manager_noti.sub1;
+ }
+ }
+
+ if (manager_noti.dest == CCIC_NOTIFY_DEV_USB_DP) {
+ if (manager_noti.id == CCIC_NOTIFY_ID_USB_DP) {
+ typec_manager.dp_is_connect = manager_noti.sub1;
+ typec_manager.dp_hs_connect = manager_noti.sub2;
+ }
+ }
+
+ if (manager_noti.dest == CCIC_NOTIFY_DEV_USB) {
+ if (typec_manager.ccic_drp_state == manager_noti.sub2)
+ return 0;
+ typec_manager.ccic_drp_state = manager_noti.sub2;
+ if (typec_manager.ccic_drp_state == USB_STATUS_NOTIFY_DETACH)
+ set_usb_enumeration_state(0);
+ }
+
+ if (manager_noti.dest == CCIC_NOTIFY_DEV_BATTERY
+ && manager_noti.sub3 == ATTACHED_DEV_UNDEFINED_RANGE_MUIC) {
+ if (manager_noti.sub1 != typec_manager.wVbus_det) {
+ typec_manager.wVbus_det = manager_noti.sub1;
+ typec_manager.waterChg_count += manager_noti.sub1;
+ wVbus_time_update(typec_manager.wVbus_det);
+ } else {
+ return 0;
+ }
+ }
+
+#ifdef CONFIG_USB_NOTIFY_PROC_LOG
+ store_usblog_notify(NOTIFY_MANAGER, (void*)data , NULL);
+#endif
+
+ if (manager_noti.dest == CCIC_NOTIFY_DEV_MUIC) {
+ ret = blocking_notifier_call_chain(&(typec_manager.manager_muic_notifier),
+ manager_noti.id, &manager_noti);
+ } else {
+ ret = blocking_notifier_call_chain(&(typec_manager.manager_ccic_notifier),
+ manager_noti.id, &manager_noti);
+ }
+
+ switch (ret) {
+ case NOTIFY_DONE:
+ case NOTIFY_OK:
+ pr_info("usb: [M] %s: notify done(0x%x)\n", __func__, ret);
+ break;
+ case NOTIFY_STOP_MASK:
+ case NOTIFY_BAD:
+ default:
+ if ( manager_noti.dest == CCIC_NOTIFY_DEV_USB) {
+ pr_info("usb: [M] %s: UPSM case (0x%x)\n", __func__, ret);
+ typec_manager.is_UFPS = 1;
+ } else {
+ pr_info("usb: [M] %s: notify error occur(0x%x)\n", __func__, ret);
+ }
+ break;
+ }
+
+ return ret;
+}
+
+void set_usb_enumeration_state(int state)
+{
+ if(typec_manager.usb_enum_state != state) {
+ typec_manager.usb_enum_state = state;
+
+ if(typec_manager.usb_enum_state == 0x310)
+ typec_manager.usb310_count++;
+ else if(typec_manager.usb_enum_state == 0x210)
+ typec_manager.usb210_count++;
+ }
+}
+EXPORT_SYMBOL(set_usb_enumeration_state);
+
+bool get_usb_enumeration_state(void)
+{
+ return typec_manager.usb_enum_state? 1: 0;
+}
+EXPORT_SYMBOL(get_usb_enumeration_state);
+
+int get_ccic_water_count(void)
+{
+ int ret;
+ ret = typec_manager.water_count;
+ typec_manager.water_count = 0;
+ return ret;
+}
+EXPORT_SYMBOL(get_ccic_water_count);
+
+int get_ccic_dry_count(void)
+{
+ int ret;
+ ret = typec_manager.dry_count;
+ typec_manager.dry_count = 0;
+ return ret;
+}
+EXPORT_SYMBOL(get_ccic_dry_count);
+
+int get_usb210_count(void)
+{
+ int ret;
+ ret = typec_manager.usb210_count;
+ typec_manager.usb210_count = 0;
+ return ret;
+}
+EXPORT_SYMBOL(get_usb210_count);
+
+int get_usb310_count(void)
+{
+ int ret;
+ ret = typec_manager.usb310_count;
+ typec_manager.usb310_count = 0;
+ return ret;
+}
+EXPORT_SYMBOL(get_usb310_count);
+
+int get_waterChg_count(void)
+{
+ unsigned int ret;
+ ret = typec_manager.waterChg_count;
+ typec_manager.waterChg_count = 0;
+ return ret;
+}
+EXPORT_SYMBOL(get_waterChg_count);
+
+unsigned long get_waterDet_duration(void)
+{
+ unsigned long ret;
+ struct timeval time;
+
+ if (typec_manager.water_det) {
+ do_gettimeofday(&time);
+ calc_duration_time(typec_manager.waterDet_time,
+ time.tv_sec, &typec_manager.waterDet_duration);
+ typec_manager.waterDet_time = time.tv_sec;
+ }
+
+ ret = typec_manager.waterDet_duration/60; /* min */
+ typec_manager.waterDet_duration -= ret*60;
+ return ret;
+}
+EXPORT_SYMBOL(get_waterDet_duration);
+
+unsigned long get_wVbus_duration(void)
+{
+ unsigned long ret;
+ struct timeval time;
+
+ if (typec_manager.wVbus_det) {
+ do_gettimeofday(&time); /* time.tv_sec */
+ calc_duration_time(typec_manager.wVbusHigh_time,
+ time.tv_sec, &typec_manager.wVbus_duration);
+ typec_manager.wVbusHigh_time = time.tv_sec;
+ }
+
+ ret = typec_manager.wVbus_duration; /* sec */
+ typec_manager.wVbus_duration = 0;
+ return ret;
+}
+EXPORT_SYMBOL(get_wVbus_duration);
+
+void set_usb_enable_state(void)
+{
+ if (!typec_manager.usb_enable_state) {
+ typec_manager.usb_enable_state = true;
+ if (typec_manager.pd_con_state) {
+ cable_type_check_work(true, 120);
+ }
+ }
+}
+EXPORT_SYMBOL(set_usb_enable_state);
+
+void calc_duration_time(unsigned long sTime, unsigned long eTime, unsigned long *dTime)
+{
+ unsigned long calcDtime;
+
+ calcDtime = eTime - sTime;
+
+ /* check for exception case. */
+ if ((calcDtime < 0) || (calcDtime > 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.src<CCIC_NOTI_DEST_NUM)? CCIC_NOTI_DEST_Print[p_noti.src]:"unknown",
+ (p_noti.dest<CCIC_NOTI_DEST_NUM)? CCIC_NOTI_DEST_Print[p_noti.dest]:"unknown",
+ (p_noti.id<CCIC_NOTI_ID_NUM)? CCIC_NOTI_ID_Print[p_noti.id]:"unknown",
+ p_noti.sub1);
+
+#if defined(CONFIG_VBUS_NOTIFIER)
+ handle_muic_fake_event(EVENT_CANCEL);
+#endif
+
+ switch (p_noti.id) {
+ case CCIC_NOTIFY_ID_POWER_STATUS:
+ if(p_noti.sub1) { /*attach*/
+ typec_manager.pd_con_state = 1; // PDIC_NOTIFY_EVENT_PD_SINK
+ if( (typec_manager.ccic_drp_state == USB_STATUS_NOTIFY_ATTACH_UFP) &&
+ !typec_manager.is_UFPS){
+ pr_info("usb: [M] %s: PD charger + UFP\n", __func__);
+ cable_type_check_work(true, 60);
+ }
+ }
+ p_noti.dest = CCIC_NOTIFY_DEV_BATTERY;
+ if(typec_manager.pd == NULL)
+ typec_manager.pd = p_noti.pd;
+ break;
+ case CCIC_NOTIFY_ID_ATTACH: // for MUIC
+ if (typec_manager.ccic_attach_state != p_noti.sub1) {
+ typec_manager.ccic_attach_state = p_noti.sub1;
+ typec_manager.muic_data_refresh = 0;
+ typec_manager.is_UFPS = 0;
+ if(typec_manager.ccic_attach_state == CCIC_NOTIFY_ATTACH){
+ pr_info("usb: [M] %s: CCIC_NOTIFY_ATTACH\n", __func__);
+ typec_manager.water_det = 0;
+ typec_manager.pd_con_state = 0;
+ }
+ }
+
+ if (typec_manager.ccic_attach_state == CCIC_NOTIFY_DETACH) {
+ pr_info("usb: [M] %s: CCIC_NOTIFY_DETACH (pd=%d, cable_type=%d)\n", __func__,
+ typec_manager.pd_con_state, typec_manager.cable_type);
+ cable_type_check_work(false, 0);
+ if (typec_manager.pd_con_state) {
+ typec_manager.pd_con_state = 0;
+ bat_noti.src = CCIC_NOTIFY_DEV_CCIC;
+ bat_noti.dest = CCIC_NOTIFY_DEV_BATTERY;
+ bat_noti.id = CCIC_NOTIFY_ID_ATTACH;
+ bat_noti.attach = CCIC_NOTIFY_DETACH;
+ bat_noti.rprd = 0;
+ bat_noti.cable_type = ATTACHED_DEV_UNOFFICIAL_ID_ANY_MUIC; // temp
+ bat_noti.pd = NULL;
+ manager_notifier_notify(&bat_noti);
+ }
+ }
+ break;
+ case CCIC_NOTIFY_ID_RID: // for MUIC (FAC)
+ typec_manager.ccic_rid_state = p_noti.sub1;
+ break;
+ case CCIC_NOTIFY_ID_USB: // for USB3
+ if ((typec_manager.cable_type == MANAGER_NOTIFY_MUIC_CHARGER)
+ || (p_noti.sub2 != USB_STATUS_NOTIFY_DETACH && /*drp */
+ (typec_manager.ccic_rid_state == RID_523K || typec_manager.ccic_rid_state == RID_619K))) {
+ return 0;
+ }
+ break;
+ case CCIC_NOTIFY_ID_WATER:
+ if (p_noti.sub1) { /* attach */
+ if(!typec_manager.water_det) {
+ typec_manager.water_det = 1;
+ typec_manager.water_count++;
+
+ muic_noti.src = CCIC_NOTIFY_DEV_CCIC;
+ muic_noti.dest = CCIC_NOTIFY_DEV_MUIC;
+ muic_noti.id = CCIC_NOTIFY_ID_WATER;
+ muic_noti.attach = CCIC_NOTIFY_ATTACH;
+ muic_noti.rprd = 0;
+ muic_noti.cable_type = 0;
+ muic_noti.pd = NULL;
+ manager_notifier_notify(&muic_noti);
+
+ /*update water time */
+ water_dry_time_update((int)p_noti.sub1);
+
+ if (typec_manager.muic_action == MUIC_NOTIFY_CMD_ATTACH) {
+ p_noti.sub3 = ATTACHED_DEV_UNDEFINED_RANGE_MUIC; /* cable_type */
+ } else {
+ /* If the cable is not connected, skip the battery event. */
+ return 0;
+ }
+ } else {
+ /* Ignore duplicate events */
+ return 0;
+ }
+ } else {
+ typec_manager.water_det = 0;
+ typec_manager.dry_count++;
+
+ /* update run_dry time */
+ water_dry_time_update((int)p_noti.sub1);
+
+ if (typec_manager.wVbus_det)
+ p_noti.sub3 = ATTACHED_DEV_UNDEFINED_RANGE_MUIC;
+ else
+ return 0;
+ }
+ break;
+ default:
+ break;
+ }
+
+ ret = manager_notifier_notify(&p_noti);
+
+ return ret;
+}
+
+static int manager_handle_muic_notification(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ CC_NOTI_ATTACH_TYPEDEF p_noti = *(CC_NOTI_ATTACH_TYPEDEF *)data;
+ CC_NOTI_USB_STATUS_TYPEDEF usb_noti;
+
+ pr_info("usb: [M] %s: attach:%d, cable_type:%d\n", __func__,
+ p_noti.attach, p_noti.cable_type);
+
+ typec_manager.muic_action = p_noti.attach;
+ typec_manager.muic_cable_type = p_noti.cable_type;
+ typec_manager.muic_data_refresh = 1;
+
+ if(typec_manager.water_det){
+ /* If Water det irq case is ignored */
+ if(p_noti.attach) typec_manager.muic_attach_state_without_ccic = 1;
+ pr_info("usb: [M] %s: Water detected case\n", __func__);
+ return 0;
+ }
+
+ if (p_noti.attach && typec_manager.ccic_drp_state == USB_STATUS_NOTIFY_DETACH) {
+ if(typec_manager.ccic_rid_state != RID_523K && typec_manager.ccic_rid_state != RID_619K)
+ typec_manager.muic_attach_state_without_ccic = 1;
+ }
+
+ switch (p_noti.cable_type) {
+ 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:
+ pr_info("usb: [M] %s: USB(%d) %s, CCIC: %s \n", __func__,
+ p_noti.cable_type, p_noti.attach ? "Attached": "Detached",
+ typec_manager.ccic_attach_state? "Attached": "Detached");
+
+ if(typec_manager.muic_action) {
+ typec_manager.cable_type = MANAGER_NOTIFY_MUIC_USB;
+ }
+
+ if(typec_manager.muic_attach_state_without_ccic) {
+ if (p_noti.attach) {
+ schedule_delayed_work(&typec_manager.muic_noti_work, msecs_to_jiffies(2000));
+ } else {
+ schedule_delayed_work(&typec_manager.muic_noti_work, 0);
+ }
+ }
+ break;
+
+ case ATTACHED_DEV_TA_MUIC:
+ pr_info("usb: [M] %s: TA(%d) %s \n", __func__, p_noti.cable_type,
+ p_noti.attach ? "Attached": "Detached");
+
+ if(typec_manager.muic_action) {
+ typec_manager.cable_type = MANAGER_NOTIFY_MUIC_CHARGER;
+ }
+
+ if(p_noti.attach && typec_manager.ccic_drp_state == USB_STATUS_NOTIFY_ATTACH_UFP ) {
+ if(typec_manager.pd_con_state) {
+ cable_type_check_work(false, 0);
+ }
+ /* Turn off the USB Phy when connected to the charger */
+ usb_noti.src = CCIC_NOTIFY_DEV_MUIC;
+ usb_noti.dest = CCIC_NOTIFY_DEV_USB;
+ usb_noti.id = CCIC_NOTIFY_ID_USB;
+ usb_noti.attach = CCIC_NOTIFY_DETACH;
+ usb_noti.drp = USB_STATUS_NOTIFY_DETACH;
+ usb_noti.sub3 = 0;
+ usb_noti.pd = NULL;
+ manager_notifier_notify(&usb_noti);
+ }
+ break;
+
+ case ATTACHED_DEV_AFC_CHARGER_PREPARE_MUIC:
+ case ATTACHED_DEV_QC_CHARGER_PREPARE_MUIC:
+ pr_info("usb: [M] %s: AFC or QC Prepare(%d) %s \n", __func__,
+ p_noti.cable_type, p_noti.attach ? "Attached": "Detached");
+ break;
+
+ case ATTACHED_DEV_TIMEOUT_OPEN_MUIC:
+ pr_info("usb: [M] %s: DCD Timeout device is detected(%d) %s \n",
+ __func__, p_noti.cable_type,
+ p_noti.attach ? "Attached": "Detached");
+
+ if(typec_manager.muic_action) {
+ typec_manager.cable_type = MANAGER_NOTIFY_MUIC_TIMEOUT_OPEN_DEVICE;
+ }
+ break;
+
+ default:
+ pr_info("usb: [M] %s: Cable(%d) %s \n", __func__, p_noti.cable_type,
+ p_noti.attach ? "Attached": "Detached");
+ break;
+ }
+ if(!typec_manager.muic_action) {
+ typec_manager.cable_type = MANAGER_NOTIFY_MUIC_NONE;
+ }
+
+ if (!(p_noti.attach) && typec_manager.ccic_attach_state && typec_manager.pd_con_state) {
+ /* If PD charger + detach case is ignored */
+ pr_info("usb: [M] %s: PD charger detached case\n", __func__);
+ } else {
+ p_noti.src = CCIC_NOTIFY_DEV_MUIC;
+ p_noti.dest = CCIC_NOTIFY_DEV_BATTERY;
+ manager_notifier_notify(&p_noti);
+ }
+
+ return 0;
+}
+
+#if defined(CONFIG_VBUS_NOTIFIER)
+static int manager_handle_vbus_notification(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ vbus_status_t vbus_type = *(vbus_status_t *)data;
+ CC_NOTI_ATTACH_TYPEDEF bat_noti;
+
+ pr_info("usb: [M] %s: cmd=%lu, vbus_type=%s, WATER DET=%d ATTACH=%s (%d)\n", __func__,
+ action, vbus_type == STATUS_VBUS_HIGH ? "HIGH" : "LOW", typec_manager.water_det,
+ typec_manager.ccic_attach_state == CCIC_NOTIFY_ATTACH ? "ATTACH":"DETATCH",
+ typec_manager.muic_attach_state_without_ccic);
+
+ typec_manager.vbus_state = vbus_type;
+
+ switch (vbus_type) {
+ case STATUS_VBUS_HIGH:
+ if (typec_manager.water_det) {
+ bat_noti.src = CCIC_NOTIFY_DEV_CCIC;
+ bat_noti.dest = CCIC_NOTIFY_DEV_BATTERY;
+ bat_noti.id = CCIC_NOTIFY_ID_WATER;
+ bat_noti.attach = CCIC_NOTIFY_ATTACH;
+ bat_noti.rprd = 0;
+ bat_noti.cable_type = ATTACHED_DEV_UNDEFINED_RANGE_MUIC;
+ bat_noti.pd = NULL;
+ manager_notifier_notify(&bat_noti);
+ }
+ break;
+ case STATUS_VBUS_LOW:
+ if (typec_manager.water_det) {
+ bat_noti.src = CCIC_NOTIFY_DEV_CCIC;
+ bat_noti.dest = CCIC_NOTIFY_DEV_BATTERY;
+ bat_noti.id = CCIC_NOTIFY_ID_ATTACH;
+ bat_noti.attach = CCIC_NOTIFY_DETACH;
+ bat_noti.rprd = 0;
+ bat_noti.cable_type = ATTACHED_DEV_UNDEFINED_RANGE_MUIC;
+ bat_noti.pd = NULL;
+ manager_notifier_notify(&bat_noti);
+ }
+ handle_muic_fake_event(EVENT_LOAD);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+#endif
+
+int manager_notifier_register(struct notifier_block *nb, notifier_fn_t notifier,
+ manager_notifier_device_t listener)
+{
+ int ret = 0;
+ MANAGER_NOTI_TYPEDEF m_noti;
+ static int alternate_mode_start_wait = 0;
+ pccic_data_t pccic_data;
+
+ pr_info("usb: [M] %s: listener=%d register\n", __func__, listener);
+ if(!manager_notifier_init_done)
+ manager_notifier_init();
+
+ ccic_notifier_init();
+ pccic_data = dev_get_drvdata(ccic_device);
+
+ /* Check if MANAGER Notifier is ready. */
+ if (!manager_device) {
+ pr_err("usb: [M] %s: Not Initialized...\n", __func__);
+ return -1;
+ }
+
+ if (listener == MANAGER_NOTIFY_CCIC_MUIC) {
+ SET_MANAGER_NOTIFIER_BLOCK(nb, notifier, listener);
+ ret = blocking_notifier_chain_register(&(typec_manager.manager_muic_notifier), nb);
+ if (ret < 0)
+ pr_err("usb: [M] %s: muic blocking_notifier_chain_register error(%d)\n",
+ __func__, ret);
+ } else {
+ SET_MANAGER_NOTIFIER_BLOCK(nb, notifier, listener);
+ ret = blocking_notifier_chain_register(&(typec_manager.manager_ccic_notifier), nb);
+ if (ret < 0)
+ pr_err("usb: [M] %s: ccic blocking_notifier_chain_register error(%d)\n",
+ __func__, ret);
+ }
+
+ /* current manager's attached_device status notify */
+ if(listener == MANAGER_NOTIFY_CCIC_BATTERY) {
+ /* CC_NOTI_ATTACH_TYPEDEF */
+ m_noti.src = CCIC_NOTIFY_DEV_MANAGER;
+ m_noti.dest = CCIC_NOTIFY_DEV_BATTERY;
+ m_noti.sub1 = (typec_manager.ccic_attach_state || typec_manager.muic_action);
+ m_noti.sub2 = 0;
+ m_noti.sub3 = 0;
+ m_noti.pd = typec_manager.pd;
+ if(typec_manager.water_det && m_noti.sub1) {
+ m_noti.id = CCIC_NOTIFY_ID_WATER;
+ m_noti.sub3 = ATTACHED_DEV_UNDEFINED_RANGE_MUIC;
+ } else {
+ m_noti.id = CCIC_NOTIFY_ID_ATTACH;
+ if(typec_manager.pd_con_state) {
+ pr_info("usb: [M] %s: PD is attached already\n", __func__);
+ m_noti.id = CCIC_NOTIFY_ID_POWER_STATUS;
+ } else if(typec_manager.muic_cable_type != ATTACHED_DEV_NONE_MUIC) {
+ m_noti.sub3= typec_manager.muic_cable_type;
+ } else {
+ switch(typec_manager.ccic_drp_state){
+ case USB_STATUS_NOTIFY_ATTACH_UFP:
+ m_noti.sub3 = ATTACHED_DEV_USB_MUIC;
+ break;
+ case USB_STATUS_NOTIFY_ATTACH_DFP:
+ m_noti.sub3 = ATTACHED_DEV_OTG_MUIC;
+ break;
+ default:
+ m_noti.sub3 = ATTACHED_DEV_NONE_MUIC;
+ break;
+ }
+ }
+ }
+ pr_info("usb: [M] %s BATTERY: cable_type=%d (%s) \n", __func__, m_noti.sub3,
+ typec_manager.muic_cable_type? "MUIC" : "CCIC");
+ nb->notifier_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);
+
--- /dev/null
+#
+# 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.
--- /dev/null
+
+# 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
+
--- /dev/null
+/*
+ * 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 <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/usb.h>
+#include <linux/notifier.h>
+#include <linux/version.h>
+#include <linux/usb_notify.h>
+#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);
+
--- /dev/null
+/*
+ * 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
+
--- /dev/null
+/*
+ * 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 <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/usb.h>
+#include <linux/usb_notify.h>
+
+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);
--- /dev/null
+/*
+ * drivers/usb/notify/host_notify_class.c
+ *
+ * Copyright (C) 2011-2017 Samsung, Inc.
+ * Author: Dongrak Shin <dongrak.shin@samsung.com>
+ *
+*/
+
+ /* usb notify layer v3.1 */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/err.h>
+#include <linux/host_notify.h>
+#if defined(CONFIG_USB_HW_PARAM)
+#include <linux/usb_notify.h>
+#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 <dongrak.shin@samsung.com>");
+MODULE_DESCRIPTION("Usb host notify driver");
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * Copyright (C) 2011 Samsung Electronics Co. Ltd.
+ * Inchul Im <inchul.im@samsung.com>
+ *
+ * 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 <linux/module.h>
+#include <linux/delay.h>
+#include <linux/usb_notify.h>
+#ifdef CONFIG_OF
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#endif
+#if defined(CONFIG_IFCONN_NOTIFIER)
+#include <linux/ifconn/ifconn_notifier.h>
+#endif
+#if defined(CONFIG_CCIC_NOTIFIER)
+#include <linux/ccic/ccic_notifier.h>
+#endif
+#if defined(CONFIG_MUIC_NOTIFIER) || defined(CONFIG_IFCONN_NOTIFIER)
+#include <linux/muic/muic.h>
+#endif
+#if defined(CONFIG_MUIC_NOTIFIER)
+#include <linux/muic/muic_notifier.h>
+#endif
+#if defined(CONFIG_VBUS_NOTIFIER)
+#include <linux/vbus_notifier.h>
+#endif
+#if defined(CONFIG_USB_TYPEC_MANAGER_NOTIFIER)
+#include <linux/usb/manager/usb_typec_manager_notifier.h>
+#endif
+#if defined(CONFIG_BATTERY_SAMSUNG_V2)
+#include "../../battery_v2/include/sec_charging_common.h"
+#else
+#include <linux/battery/sec_charging_common.h>
+#endif
+#include "usb_notifier.h"
+
+#include <linux/regulator/consumer.h>
+#include <linux/workqueue.h>
+
+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 <inchul.im@samsung.com>");
+MODULE_DESCRIPTION("USB notifier");
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * 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
+
--- /dev/null
+/*
+ * 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 <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/err.h>
+#include <linux/wakelock.h>
+#include <linux/kthread.h>
+#include <linux/usb_notify.h>
+#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");
--- /dev/null
+/*
+ * drivers/usb/notify/usb_notify_sysfs.c
+ *
+ * Copyright (C) 2015-2017 Samsung, Inc.
+ * Author: Dongrak Shin <dongrak.shin@samsung.com>
+ *
+*/
+
+ /* usb notify layer v3.1 */
+
+#define pr_fmt(fmt) "usb_notify: " fmt
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/err.h>
+#include <linux/usb.h>
+#include <linux/usb_notify.h>
+#include <linux/string.h>
+#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);
+
--- /dev/null
+/*
+ * 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
+
--- /dev/null
+/*
+ * drivers/usb/notify/usblog_proc_notify.c
+ *
+ * Copyright (C) 2016-2017 Samsung, Inc.
+ * Author: Dongrak Shin <dongrak.shin@samsung.com>
+ *
+*/
+
+ /* usb notify layer v3.1 */
+
+ #define pr_fmt(fmt) "usb_notify: " fmt
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/time.h>
+#include <linux/kernel.h>
+#include <linux/security.h>
+#include <linux/syscalls.h>
+#include <linux/proc_fs.h>
+#include <linux/spinlock.h>
+#include <linux/seq_file.h>
+#include <linux/usb_notify.h>
+
+#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);
+
--- /dev/null
+/*
+ * 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 <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/power_supply.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+
+/* 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 */
--- /dev/null
+/*
+ * 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 <linux/notifier.h>
+
+/* 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__ */
--- /dev/null
+/*
+ * Host notify class driver
+ *
+ * Copyright (C) 2011-2017 Samsung, Inc.
+ * Author: Dongrak Shin <dongrak.shin@samsung.com>
+ *
+*/
+
+ /* 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__ */
--- /dev/null
+/*
+ * 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 <linux/i2c.h>
+#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 */
+
--- /dev/null
+/*
+ * include/linux/muic/ccic_notifier.h
+ *
+ * header file supporting CCIC notifier call chain information
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ * Seung-Jin Hahn <sjin.hahn@samsung.com>
+ *
+ * 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 <linux/ccic/ccic_notifier.h>
+#include <linux/muic/muic.h>
+#include <linux/muic/muic_notifier.h>
+#ifdef CONFIG_VBUS_NOTIFIER
+#include <linux/vbus_notifier.h>
+#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__ */
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * usb notify header
+ *
+ * Copyright (C) 2011-2017 Samsung, Inc.
+ * Author: Dongrak Shin <dongrak.shin@samsung.com>
+ *
+*/
+
+ /* usb notify layer v3.1 */
+
+#ifndef __LINUX_USB_NOTIFY_H__
+#define __LINUX_USB_NOTIFY_H__
+
+#include <linux/notifier.h>
+#include <linux/host_notify.h>
+#include <linux/external_notify.h>
+#include <linux/usblog_proc_notify.h>
+#if defined(CONFIG_USB_HW_PARAM)
+#include <linux/usb_hw_param.h>
+#endif
+#include <linux/usb.h>
+
+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__ */
--- /dev/null
+/*
+ * 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
+