[9610][ERD] driver/dts: muic/ifconn: bring up drvs
authorKim Taejeong <tj.kim@samsung.com>
Mon, 14 May 2018 00:26:48 +0000 (09:26 +0900)
committerKisang Lee <kisang80.lee@samsung.com>
Thu, 17 May 2018 12:22:40 +0000 (21:22 +0900)
Change-Id: I8715f6ec40d73456e82ea58aa92afa9012aee3d1
Signed-off-by: Kim Taejeong <tj.kim@samsung.com>
33 files changed:
arch/arm64/boot/dts/exynos/exynos9610-erd9610.dts
drivers/Kconfig
drivers/Makefile
drivers/ifconn/Kconfig [new file with mode: 0644]
drivers/ifconn/Makefile [new file with mode: 0644]
drivers/ifconn/ifconn_manager.c [new file with mode: 0644]
drivers/ifconn/ifconn_notifier.c [new file with mode: 0644]
drivers/muic/Kconfig [new file with mode: 0644]
drivers/muic/Makefile [new file with mode: 0644]
drivers/muic/muic-core.c [new file with mode: 0644]
drivers/muic/muic_core.c [new file with mode: 0644]
drivers/muic/muic_manager.c [new file with mode: 0644]
drivers/muic/muic_notifier.c [new file with mode: 0644]
drivers/muic/muic_sysfs.c [new file with mode: 0644]
drivers/muic/s2mu004-muic-hv.c [new file with mode: 0644]
drivers/muic/s2mu004-muic-notifier.c [new file with mode: 0644]
drivers/muic/s2mu004-muic-sysfs.c [new file with mode: 0644]
drivers/muic/s2mu004-muic.c [new file with mode: 0644]
drivers/power/supply/s2mu00x_battery.c
include/linux/ccic/ccic_notifier.h [new file with mode: 0644]
include/linux/ifconn/ifconn_manager.h [new file with mode: 0644]
include/linux/ifconn/ifconn_notifier.h [new file with mode: 0644]
include/linux/muic/muic.h [new file with mode: 0644]
include/linux/muic/muic_core.h [new file with mode: 0644]
include/linux/muic/muic_interface.h [new file with mode: 0644]
include/linux/muic/muic_notifier.h [new file with mode: 0644]
include/linux/muic/muic_sysfs.h [new file with mode: 0644]
include/linux/muic/s2mu004-muic-hv.h [new file with mode: 0644]
include/linux/muic/s2mu004-muic-notifier.h [new file with mode: 0644]
include/linux/muic/s2mu004-muic-sysfs.h [new file with mode: 0644]
include/linux/muic/s2mu004-muic.h [new file with mode: 0644]
include/linux/sec_sysfs.h [new file with mode: 0644]
kernel/panic.c

index c387ed3835aad280462717e2a5ccedf57cc054aa..dff304275ce4a24cb146b75f1c4e76274004656d 100644 (file)
@@ -27,7 +27,7 @@
        };
 
        chosen {
-               bootargs = "console=ttySAC0,115200n8 root=/dev/ram0 clk_ignore_unused bcm_setup=0xffffff80f8e00000 androidboot.hardware=samsungexynos9610 androidboot.selinux=permissive androidboot.debug_level=0x4948 firmware_class.path=/vendor/firmware ecd_setup=disable reserve-fimc=0xffffff80fa000000";
+               bootargs = "console=ttySAC0,115200n8 root=/dev/ram0 clk_ignore_unused bcm_setup=0xffffff80f8e00000 androidboot.hardware=samsungexynos9610 androidboot.selinux=permissive androidboot.debug_level=0x4948 firmware_class.path=/vendor/firmware ecd_setup=disable reserve-fimc=0xffffff80fa000000 pmic_info=0x3";
                linux,initrd-start = <0x84000000>;
                linux,initrd-end = <0x841FFFFF>;
        };
                };
        };
 
+
+       ifconn {
+               status = "okay";
+               compatible = "samsung,ifconn";
+               ifconn,usbpd = "s2mm005";
+               ifconn,muic = "s2mu004-muic";
+       };
+
        speedy@11a10000 {
                status = "okay";
 
                s2mu004,wakeup;
        };
 
+       muic {
+               status = "okay";
+               muic,uart_addr = "11850000.pinctrl";
+               muic,uart_rxd = "gpq0-3";
+               muic,uart_txd = "gpq0-4";
+       };
+
        s2mu004-charger {
                status = "okay";
                battery,charger_name = "s2mu004-charger";
index f37c3bd57988d8e86f594c8fc0d50348e3b94dde..30bc61e5c12f36edf3787497b9ff33265b282d87 100644 (file)
@@ -25,6 +25,9 @@ source "drivers/nvme/Kconfig"
 
 source "drivers/misc/Kconfig"
 
+source "drivers/muic/Kconfig"
+
+source "drivers/ifconn/Kconfig"
 source "drivers/ide/Kconfig"
 
 source "drivers/scsi/Kconfig"
index 7a89cd30e6ef25caaf39069b03897e52260fbd07..9518860e6db502caeaf09d186dd1e1ab2b74b0af 100644 (file)
@@ -70,6 +70,8 @@ obj-$(CONFIG_CONNECTOR)               += connector/
 obj-$(CONFIG_FB_I810)           += video/fbdev/i810/
 obj-$(CONFIG_FB_INTEL)          += video/fbdev/intelfb/
 
+obj-$(CONFIG_USE_MUIC)         += muic/
+obj-$(CONFIG_IFCONN_MANAGER)    += ifconn/
 obj-$(CONFIG_PARPORT)          += parport/
 obj-$(CONFIG_NVM)              += lightnvm/
 obj-y                          += base/ block/ misc/ mfd/ nfc/
diff --git a/drivers/ifconn/Kconfig b/drivers/ifconn/Kconfig
new file mode 100644 (file)
index 0000000..c433733
--- /dev/null
@@ -0,0 +1,29 @@
+#
+# IF Conn Manager driver
+#
+
+config IFCONN_MANAGER
+    bool "IF Conn Manager driver"
+       depends on I2C
+       default n
+       help
+         If you say yes here you will get support for
+         IF Conn Manager function.
+
+config IFCONN_NOTIFIER
+    bool "IF Conn Notifier driver"
+       depends on I2C
+       default n
+    help
+      If you say yes here you will get support for
+      IF Conn Notifier function.
+
+config IFCONN_SUPPORT_THREAD
+    bool "IF Conn driver supports thread"
+       depends on I2C
+       default n
+    help
+      If you say yes here you will get support for
+      IF Conn thread function.
+      This makes the module concurrent.
+      Take a module test when take this option.
diff --git a/drivers/ifconn/Makefile b/drivers/ifconn/Makefile
new file mode 100644 (file)
index 0000000..25ef9cf
--- /dev/null
@@ -0,0 +1,6 @@
+#
+# Makefile for usb typec manager drivers
+#
+
+obj-$(CONFIG_IFCONN_MANAGER)     += ifconn_manager.o
+obj-$(CONFIG_IFCONN_NOTIFIER)     += ifconn_notifier.o
diff --git a/drivers/ifconn/ifconn_manager.c b/drivers/ifconn/ifconn_manager.c
new file mode 100644 (file)
index 0000000..952dd88
--- /dev/null
@@ -0,0 +1,735 @@
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/ifconn/ifconn_notifier.h>
+#include <linux/ifconn/ifconn_manager.h>
+#include <linux/muic/muic.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/time.h>
+#include <linux/ktime.h>
+#include <linux/rtc.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#if defined(CONFIG_CCIC_NOTIFIER)
+#include <linux/ccic/core.h>
+#endif
+
+static int _ifconn_manager_template_notify(struct ifconn_notifier_template *template)
+{
+       int ret;
+
+       if (template == NULL) {
+               pr_info("%s: err, template is NULL\n", __func__);
+               return -1;
+       }
+
+       ret = ifconn_notifier_notify(template->src,
+                                    template->dest,
+                                    template->id,
+                                    template->event,
+                                    IFCONN_NOTIFY_PARAM_TEMPLATE, template);
+
+       if (ret < 0)
+               pr_err("%s: Fail to send noti\n", __func__);
+
+       return ret;
+}
+
+static void ifconn_manager_noti_work(struct ifconn_manager_data *ifconn_manager,
+                                    struct ifconn_notifier_template *template)
+{
+
+       pr_info("%s: enter, template : %p\n", __func__, template);
+       mutex_lock(&ifconn_manager->noti_mutex);
+
+       template->up_src = template->src;
+       template->src = IFCONN_NOTIFY_MANAGER;
+
+       switch (template->up_src) {
+       case IFCONN_NOTIFY_MUIC:
+               if (ifconn_manager->ccic_drp_state == IFCONN_MANAGER_USB_STATUS_DETACH) {
+                       if (template->attach && ifconn_manager->vbus_state == IFCONN_MANAGER_VBUS_STATUS_HIGH) {
+                               msleep(2000);
+                               template->drp = IFCONN_MANAGER_USB_STATUS_ATTACH_UFP;
+                               ifconn_manager->ccic_drp_state = IFCONN_MANAGER_USB_STATUS_ATTACH_UFP;
+                       } else {
+                               template->drp = IFCONN_MANAGER_USB_STATUS_DETACH;
+                               ifconn_manager->ccic_drp_state = IFCONN_MANAGER_USB_STATUS_DETACH;
+                       }
+                       template->sub3 = 0;
+                       template->data = NULL;
+               }
+               _ifconn_manager_template_notify(template);
+               break;
+       case IFCONN_NOTIFY_CCIC:
+               pr_info("%s: dbg, line : %d\n", __func__, __LINE__);
+               if ((ifconn_manager->ccic_drp_state !=
+                    IFCONN_MANAGER_USB_STATUS_ATTACH_UFP)
+                   || ifconn_manager->is_UFPS) {
+                       pr_info("usb: [M] %s: skip case\n", __func__);
+                       goto EXIT_NOTI_WORK;
+               }
+               template->src = IFCONN_NOTIFY_MANAGER;
+               template->data = NULL;
+               template->id = IFCONN_NOTIFY_ID_USB;
+               pr_info("usb: [M] %s: usb=%d, pd=%d\n", __func__,
+                       ifconn_manager->usb_enum_state,
+                       ifconn_manager->pd_con_state);
+               if (!ifconn_manager->usb_enum_state
+                   || (ifconn_manager->muic_data_refresh
+                       && ifconn_manager->cable_type ==
+                       IFCONN_CABLE_TYPE_MUIC_CHARGER)) {
+
+                       /* TA cable Type */
+                       template->dest = IFCONN_NOTIFY_USB;
+                       template->attach = IFCONN_NOTIFY_DETACH;
+                       template->drp = IFCONN_MANAGER_USB_STATUS_DETACH;
+                       template->sub3 = 0;
+               } else {
+                       /* USB cable Type */
+                       template->dest = IFCONN_NOTIFY_BATTERY;
+                       template->attach = 0;
+                       template->rprd = 0;
+                       template->cable_type = IFCONN_PD_USB_TYPE;
+               }
+               _ifconn_manager_template_notify(template);
+               break;
+       case IFCONN_NOTIFY_PDIC:
+               pr_info("%s: pdic case, up_src : %d\n", __func__,
+                       template->up_src);
+               template->src = IFCONN_NOTIFY_MANAGER;
+               template->dest = IFCONN_NOTIFY_BATTERY;
+               template->id = IFCONN_NOTIFY_ID_POWER_STATUS;
+               template->event = IFCONN_NOTIFY_EVENT_ATTACH;
+               _ifconn_manager_template_notify(template);
+               break;
+
+       default:
+               break;
+       }
+EXIT_NOTI_WORK:
+       mutex_unlock(&ifconn_manager->noti_mutex);
+}
+
+static int ifconn_manager_manage_muic(struct ifconn_manager_data *ifconn_manager,
+                                     struct ifconn_notifier_template *template)
+{
+       if (ifconn_manager == NULL || template == NULL)
+               return -1;
+
+       ifconn_manager->muic_action = template->attach;
+       ifconn_manager->muic_cable_type = template->cable_type;
+       ifconn_manager->muic_data_refresh = 1;
+
+       pr_info("%s, usb: [M] %s: cable type : (%d), id : %d\n", __func__,
+               __func__, template->cable_type,
+               template->id);
+
+       if (template->id == IFCONN_NOTIFY_ID_ATTACH) {
+               switch (template->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:
+                       if (template->attach)
+                               ifconn_manager->cable_type = IFCONN_CABLE_TYPE_MUIC_USB;
+                       template->dest = IFCONN_NOTIFY_USB;
+                       template->id = IFCONN_NOTIFY_ID_USB;
+                       ifconn_manager_noti_work(ifconn_manager, template);
+                       break;
+               case ATTACHED_DEV_TA_MUIC:
+                       pr_info("usb: [M] %s: TA(%d) %s\n", __func__,
+                               template->cable_type,
+                               template->attach ? "Attached" : "Detached");
+
+                       if (ifconn_manager->muic_action)
+                               ifconn_manager->cable_type = IFCONN_CABLE_TYPE_MUIC_CHARGER;
+
+                       if (template->attach
+                           && ifconn_manager->ccic_drp_state == IFCONN_MANAGER_USB_STATUS_ATTACH_UFP) {
+                               /* Turn off the USB Phy when connected to the charger */
+                               template->src = IFCONN_NOTIFY_MUIC;
+                               template->dest = IFCONN_NOTIFY_USB;
+                               template->id = IFCONN_NOTIFY_ID_USB;
+                               template->attach = IFCONN_NOTIFY_DETACH;
+                               template->drp = IFCONN_NOTIFY_EVENT_DETACH;
+                               template->sub3 = 0;
+                               template->data = NULL;
+                               ifconn_manager_noti_work(ifconn_manager,
+                                                        template);
+                       }
+                       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__, template->cable_type,
+                               template->attach ? "Attached" : "Detached");
+                       break;
+
+               case ATTACHED_DEV_TIMEOUT_OPEN_MUIC:
+                       pr_info
+                           ("usb: [M] %s: DCD Timeout device is detected(%d) %s\n",
+                            __func__, template->cable_type,
+                            template->attach ? "Attached" : "Detached");
+
+                       if (ifconn_manager->muic_action) {
+                               ifconn_manager->cable_type =
+                                   IFCONN_CABLE_TYPE_MUIC_TIMEOUT_OPEN_DEVICE;
+                       }
+                       break;
+
+               default:
+                       break;
+               }
+               template->id = IFCONN_NOTIFY_ID_ATTACH;
+               template->src = IFCONN_NOTIFY_MUIC;
+               template->dest = IFCONN_NOTIFY_BATTERY;
+               ifconn_manager_noti_work(ifconn_manager, template);
+
+       } else if (template->id == IFCONN_NOTIFY_ID_DETACH) {
+               template->src = IFCONN_NOTIFY_MUIC;
+               template->dest = IFCONN_NOTIFY_BATTERY;
+               ifconn_manager_noti_work(ifconn_manager, template);
+       }
+
+       return 0;
+}
+
+static int ifconn_manager_manage_battery(struct ifconn_manager_data *ifconn_manager,
+                                        struct ifconn_notifier_template *template)
+{
+       int ret;
+
+       if (ifconn_manager == NULL || template == NULL)
+               return -1;
+
+       switch (template->id) {
+       case IFCONN_NOTIFY_ID_SELECT_PDO:
+               ret = ifconn_notifier_notify(IFCONN_NOTIFY_MANAGER,
+                                            IFCONN_NOTIFY_CCIC,
+                                            template->id,
+                                            template->event,
+                                            IFCONN_NOTIFY_PARAM_TEMPLATE,
+                                            template);
+               break;
+       default:
+               break;
+       }
+       return 0;
+}
+
+static int ifconn_manager_manage_pdic(struct ifconn_manager_data *ifconn_manager,
+                                     struct ifconn_notifier_template *template)
+{
+       if (ifconn_manager == NULL || template == NULL)
+               return -1;
+
+       pr_info("%s, usb: [M] %s: cable type : (%d), id : %d\n", __func__,
+               __func__, template->cable_type,
+               template->id);
+
+       switch (template->id) {
+       case IFCONN_NOTIFY_ID_POWER_STATUS:
+               if (template->attach) {
+                       ifconn_manager->pd_con_state = 1;
+                       if ((ifconn_manager->ccic_drp_state == IFCONN_MANAGER_USB_STATUS_ATTACH_UFP)
+                           && !ifconn_manager->is_UFPS) {
+                               pr_info("usb: [M] %s: PD charger + UFP\n",
+                                       __func__);
+                       }
+               }
+               template->dest = IFCONN_NOTIFY_BATTERY;
+               if (ifconn_manager->pd == NULL)
+                       ifconn_manager->pd = template->data;
+               ifconn_manager_noti_work(ifconn_manager, template);
+               break;
+       case IFCONN_NOTIFY_ID_DP_CONNECT:
+       case IFCONN_NOTIFY_ID_DP_HPD:
+       case IFCONN_NOTIFY_ID_DP_LINK_CONF:
+               template->src = IFCONN_NOTIFY_MANAGER;
+               template->dest = IFCONN_NOTIFY_DP;
+               _ifconn_manager_template_notify(template);
+               break;
+       default:
+               break;
+       }
+       return 0;
+}
+
+static int ifconn_manager_manage_ccic(struct ifconn_manager_data *ifconn_manager,
+                                     struct ifconn_notifier_template *template)
+{
+       if (ifconn_manager == NULL || template == NULL)
+               return -1;
+
+       switch (template->id) {
+       case IFCONN_NOTIFY_ID_ATTACH:
+               if (ifconn_manager->ccic_attach_state != template->sub1) {
+                       /*attach */
+                       ifconn_manager->ccic_attach_state = template->sub1;
+                       ifconn_manager->muic_data_refresh = 0;
+                       ifconn_manager->is_UFPS = 0;
+                       if (ifconn_manager->ccic_attach_state == IFCONN_NOTIFY_ATTACH) {
+                               pr_info("usb: [M] %s: IFCONN_NOTIFY_ATTACH\n", __func__);
+                               ifconn_manager->water_det = 0;
+                               ifconn_manager->pd_con_state = 0;
+                       } else {        /* IFCONN_NOTIFY_DETACH */
+                               pr_info
+                                   ("usb: [M] %s: IFCONN_NOTIFY_DETACH (pd=%d, cable_type=%d)\n",
+                                    __func__, ifconn_manager->pd_con_state,
+                                    ifconn_manager->cable_type);
+                               if (ifconn_manager->pd_con_state) {
+                                       ifconn_manager->pd_con_state = 0;
+                                       template->src = IFCONN_NOTIFY_CCIC;
+                                       template->dest = IFCONN_NOTIFY_BATTERY;
+                                       template->id = IFCONN_NOTIFY_ID_ATTACH;
+                                       template->attach = IFCONN_NOTIFY_DETACH;
+                                       template->rprd = 0;
+                                       template->cable_type = ATTACHED_DEV_UNOFFICIAL_ID_ANY_MUIC;
+                                       template->data = NULL;
+                               }
+                       }
+               }
+               break;
+
+       case IFCONN_NOTIFY_ID_DETACH:
+               template->src = IFCONN_NOTIFY_MANAGER;
+               template->dest = IFCONN_NOTIFY_BATTERY;
+               _ifconn_manager_template_notify(template);
+               return 0;
+
+       case IFCONN_NOTIFY_ID_RID:
+               ifconn_manager->ccic_rid_state = template->sub1;
+               break;
+
+       case IFCONN_NOTIFY_ID_USB:
+               if ((ifconn_manager->cable_type ==
+                    IFCONN_CABLE_TYPE_MUIC_CHARGER)
+                   || (template->sub2 != IFCONN_MANAGER_USB_STATUS_DETACH      /*drp */
+                       && (ifconn_manager->ccic_rid_state ==
+                           IFCONN_MANAGER_RID_523K
+                           || ifconn_manager->ccic_rid_state ==
+                           IFCONN_MANAGER_RID_619K))) {
+                       return 0;
+               }
+               break;
+
+       case IFCONN_NOTIFY_ID_WATER:
+               if (template->sub1) {   /* attach */
+#ifdef CONFIG_IFCONN_WATER_EVENT_ENABLE
+                       if (!ifconn_manager->water_det) {
+                               ifconn_manager->water_det = 1;
+                               ifconn_manager->water_count++;
+                               complete(&ccic_attach_done);
+
+                               template->src = IFCONN_NOTIFY_CCIC;
+                               template->dest = IFCONN_NOTIFY_MUIC;
+                               template->id = IFCONN_NOTIFY_ID_WATER;
+                               template->attach = IFCONN_NOTIFY_ATTACH;
+                               template->rprd = 0;
+                               template->cable_type = 0;
+                               template->pd = NULL;
+                               ifconn_manager_noti_work(ifconn_manager, template);
+
+                               /*update water time */
+                               water_dry_time_update((int)template->sub1);
+
+                               if (ifconn_manager->muic_action == IFCONN_NOTIFY_ATTACH) {
+                                       template->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
+                       if (!ifconn_manager->water_det) {
+                               ifconn_manager->water_det = 1;
+                               if (ifconn_manager->muic_action == IFCONN_NOTIFY_ATTACH) {
+                                       template->src = IFCONN_NOTIFY_CCIC;
+                                       template->dest = IFCONN_NOTIFY_BATTERY;
+                                       template->id = IFCONN_NOTIFY_ID_ATTACH;
+                                       template->attach = IFCONN_NOTIFY_DETACH;
+                                       template->rprd = 0;
+                                       template->cable_type =
+                                           ifconn_manager->muic_cable_type;
+                                       template->data = NULL;
+                                       ifconn_manager_noti_work(ifconn_manager,
+                                                                template);
+                               }
+                       }
+                       return 0;
+#endif
+               } else {
+                       ifconn_manager->water_det = 0;
+                       ifconn_manager->dry_count++;
+                       return 0;
+               }
+               break;
+       default:
+               break;
+       }
+
+       ifconn_manager_noti_work(ifconn_manager, template);
+       return 0;
+}
+
+static int ifconn_manager_manage_vbus(struct ifconn_manager_data *ifconn_manager,
+                                     struct ifconn_notifier_template *template)
+{
+       ifconn_notifier_vbus_t vbus_type = template->vbus_type;
+
+       pr_info("usb: [M] %s: vbus_type=%s, WATER DET=%d ATTACH=%s (%d)\n",
+               __func__, vbus_type == IFCONN_NOTIFY_VBUS_HIGH ? "HIGH" : "LOW",
+               ifconn_manager->water_det,
+               ifconn_manager->ccic_attach_state ==
+               IFCONN_NOTIFY_ATTACH ? "ATTACH" : "DETATCH",
+               ifconn_manager->muic_attach_state_without_ccic);
+
+       ifconn_manager->vbus_state = vbus_type;
+
+#ifdef CONFIG_IFCONN_WATER_EVENT_ENABLE
+       init_completion(&ccic_attach_done);
+       if ((ifconn_manager->water_det == 1)
+           && (template->vbus_type == IFCONN_NOTIFY_VBUS_HIGH))
+               wait_for_completion_timeout(&ccic_attach_done,
+                                           msecs_to_jiffies(2000));
+
+       switch (vbus_type) {
+       case IFCONN_NOTIFY_VBUS_HIGH:
+               if (ifconn_manager->water_det) {
+                       ifconn_manager->waterChg_count++;
+                       template->src = IFCONN_NOTIFY_CCIC;
+                       template->dest = IFCONN_NOTIFY_BATTERY;
+                       template->id = IFCONN_NOTIFY_ID_WATER;
+                       template->attach = IFCONN_NOTIFY_ATTACH;
+                       template->rprd = 0;
+                       template->cable_type =
+                           ATTACHED_DEV_UNDEFINED_RANGE_MUIC;
+                       template->data = NULL;
+                       _ifconn_manager_template_notify(template);
+               }
+               break;
+       case IFCONN_NOTIFY_VBUS_LOW:
+               if (ifconn_manager->water_det) {
+                       template->src = IFCONN_NOTIFY_CCIC;
+                       template->dest = IFCONN_NOTIFY_BATTERY;
+                       template->id = IFCONN_NOTIFY_ID_ATTACH;
+                       template->attach = IFCONN_NOTIFY_DETACH;
+                       template->rprd = 0;
+                       template->cable_type =
+                           ATTACHED_DEV_UNDEFINED_RANGE_MUIC;
+                       template->data = NULL;
+                       _ifconn_manager_template_notify(template);
+               }
+               break;
+       default:
+               break;
+       }
+#endif
+       return 0;
+}
+
+static int ifconn_manager_parse_dt(struct ifconn_manager_data *ifconn_manager)
+{
+       struct device_node *np = ifconn_manager->dev->of_node;
+       struct ifconn_manager_platform_data *pdata = ifconn_manager->pdata;
+       int ret = 0;
+
+       if (!np)
+               return -1;
+
+       ret = of_property_read_string(np,
+                                     "ifconn,usbpd",
+                                     (char const **)&pdata->usbpd_name);
+
+       if (ret < 0)
+               goto err;
+
+       ret = of_property_read_string(np,
+                                     "ifconn,muic",
+                                     (char const **)&pdata->muic_name);
+
+err:
+       return ret;
+}
+
+#ifdef CONFIG_IFCONN_SUPPORT_THREAD
+static struct ifconn_notifier_template *_ifconn_manager_dequeue_template
+       (struct ifconn_manager_data *ifconn_manager)
+{
+       struct ifconn_notifier_template *target = NULL;
+       struct ifconn_manager_template *step = NULL;
+
+       if (ifconn_manager->hp) {
+               pr_info("%s: ifconn_manager->hp : %p!!!!!\n", __func__, ifconn_manager->hp);
+               pr_info("%s: ifconn_manager->hp->node : %p!!!!!\n", __func__, &ifconn_manager->hp->node);
+               _ifconn_show_attr(&ifconn_manager->hp->node);
+
+               if (ifconn_manager->hp == ifconn_manager->tp)
+                       step = NULL;
+               else
+                       step = (struct ifconn_manager_template *)ifconn_manager->hp->np;
+               target = &ifconn_manager->hp->node;
+               //kfree(ifconn_manager->hp);
+               ifconn_manager->hp = step;
+               ifconn_manager->template_cnt--;
+       }
+       pr_info("%s: target : %p!!!!!\n", __func__, target);
+       _ifconn_show_attr(target);
+       return target;
+}
+
+static void _ifconn_manager_enqueue_template
+       (struct ifconn_manager_data *ifconn_manager, struct ifconn_notifier_template *template)
+{
+       struct ifconn_manager_template *template_ifc = NULL;
+
+       template_ifc = kzalloc(sizeof(struct ifconn_manager_template), GFP_KERNEL);
+       pr_info("%s: enter --- line : %d!!!!!\n", __func__,  __LINE__);
+
+       memcpy(&template_ifc->node, template,
+               sizeof(struct ifconn_notifier_template));
+
+       if (template->data != NULL) {
+               switch (template->id) {
+               case IFCONN_NOTIFY_ID_POWER_STATUS:
+                       memcpy(template_ifc->node.data, template->data,
+                               sizeof(usbpd_pdic_sink_state_t));
+                       break;
+               case IFCONN_NOTIFY_ID_DP_CONNECT:
+               case IFCONN_NOTIFY_ID_DP_HPD:
+               case IFCONN_NOTIFY_ID_DP_LINK_CONF:
+                       memcpy(template_ifc->node.data, template->data,
+                               sizeof(struct usbpd_info));
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       if (ifconn_manager->hp == NULL) {
+               ifconn_manager->tp = ifconn_manager->hp = template_ifc;
+               ifconn_manager->template_cnt++;
+       } else {
+               if (ifconn_manager->template_cnt < TEMPLATE_MAX)
+                       ifconn_manager->template_cnt++;
+               else
+                       _ifconn_manager_dequeue_template(ifconn_manager);
+               template_ifc->rp = (void *)ifconn_manager->tp;
+               ifconn_manager->tp->np = (void *)template_ifc;
+               ifconn_manager->tp = template_ifc;
+       }
+       pr_info("%s: hp : %p!!!!!\n", __func__, ifconn_manager->hp);
+       pr_info("%s: tp : %p!!!!!\n", __func__, ifconn_manager->tp);
+       pr_info("%s: ifconn_manager->hp->node : %p!!!!!\n", __func__, &ifconn_manager->hp->node);
+       _ifconn_show_attr(&ifconn_manager->hp->node);
+       _ifconn_show_attr(&ifconn_manager->tp->node);
+}
+#endif
+
+static void ifconn_manager_work(struct work_struct *work)
+{
+       struct ifconn_manager_data *ifconn_manager =
+           container_of(work, struct ifconn_manager_data, noti_work);
+       struct ifconn_notifier_template *template = NULL;
+
+
+       pr_info("%s: enter --- line : %d!!!!!\n", __func__,  __LINE__);
+
+       template = ifconn_manager->template;
+
+#ifdef CONFIG_IFCONN_SUPPORT_THREAD
+       if (ifconn_manager->template != NULL)
+               template = ifconn_manager->template;
+       else
+               template = _ifconn_manager_dequeue_template(ifconn_manager);
+       pr_info("%s: enter      line : %d!!!!!\n", __func__, __LINE__);
+
+#endif
+
+       switch (template->src) {
+       case IFCONN_NOTIFY_PDIC:
+               ifconn_manager_manage_pdic(ifconn_manager, template);
+               break;
+       case IFCONN_NOTIFY_MUIC:
+               ifconn_manager_manage_muic(ifconn_manager, template);
+               break;
+       case IFCONN_NOTIFY_CCIC:
+               ifconn_manager_manage_ccic(ifconn_manager, template);
+               break;
+       case IFCONN_NOTIFY_VBUS:
+               ifconn_manager_manage_vbus(ifconn_manager, template);
+               break;
+       case IFCONN_NOTIFY_BATTERY:
+               ifconn_manager_manage_battery(ifconn_manager, template);
+               break;
+       case IFCONN_NOTIFY_DP:
+               break;
+       case IFCONN_NOTIFY_USB_DP:
+               break;
+       default:
+               break;
+       }
+}
+
+static int ifconn_manager_isr(struct notifier_block *nb,
+                             unsigned long action, void *data)
+{
+       struct ifconn_manager_data *ifconn_manager =
+           container_of(nb, struct ifconn_manager_data, nb);
+       struct ifconn_notifier_template *template =
+           (struct ifconn_notifier_template *)data;
+
+       if (data == NULL) {
+               pr_info("%s: data NULL\n", __func__);
+               return -1;
+       }
+
+       pr_info("%s: enter src : %d\n", __func__, template->src);
+#ifdef CONFIG_IFCONN_SUPPORT_THREAD
+       mutex_lock(&ifconn_manager->enqueue_mutex);
+       ifconn_manager->template = NULL;
+       if (template->event     == IFCONN_NOTIFY_EVENT_IN_HURRY) {
+#endif
+               ifconn_manager->template = template;
+               ifconn_manager_work(&ifconn_manager->noti_work);
+#ifdef CONFIG_IFCONN_SUPPORT_THREAD
+       } else {
+               pr_info("%s: enter src : %d, line : %d!!!!!\n", __func__, template->src, __LINE__);
+
+               _ifconn_manager_enqueue_template(ifconn_manager, template);
+               schedule_work(&ifconn_manager->noti_work);
+       }
+
+       mutex_unlock(&ifconn_manager->enqueue_mutex);
+#endif
+
+       return 0;
+}
+
+static int ifconn_manager_probe(struct platform_device *pdev)
+{
+       struct ifconn_manager_data *ifconn_manager;
+       struct ifconn_manager_platform_data *pdata = NULL;
+       int ret = 0, i = 0;
+
+       pr_info("%s: enter\n", __func__);
+
+       ifconn_manager = kzalloc(sizeof(*ifconn_manager), GFP_KERNEL);
+       if (!ifconn_manager)
+               return -ENOMEM;
+
+       ifconn_manager->dev = &pdev->dev;
+       dev_set_drvdata(ifconn_manager->dev, ifconn_manager);
+
+       if (pdev->dev.of_node) {
+               pdata = devm_kzalloc(&pdev->dev,
+                                    sizeof(struct ifconn_manager_platform_data),
+                                    GFP_KERNEL);
+               if (!pdata) {
+                       dev_err(&pdev->dev, "Failed to allocate memory\n");
+                       ret = -ENOMEM;
+                       goto ERROR_PDATA_FREE;
+               }
+
+               ifconn_manager->pdata = pdata;
+
+               if (ifconn_manager_parse_dt(ifconn_manager)) {
+                       dev_err(&pdev->dev,
+                               "%s: Failed to get ifconn manager dt\n", __func__);
+                       ret = -EINVAL;
+                       goto ERROR_IFCONN_FREE;
+               }
+       } else {
+               pdata = dev_get_platdata(&pdev->dev);
+               ifconn_manager->pdata = pdata;
+       }
+
+       if (ret < 0)
+               goto ERROR_PDATA_FREE;
+
+       mutex_init(&ifconn_manager->noti_mutex);
+       mutex_init(&ifconn_manager->workqueue_mutex);
+#ifdef CONFIG_IFCONN_SUPPORT_THREAD
+       mutex_init(&ifconn_manager->enqueue_mutex);
+       INIT_WORK(&ifconn_manager->noti_work, ifconn_manager_work);
+       ifconn_manager->hp = NULL;
+       ifconn_manager->tp = NULL;
+       ifconn_manager->template = NULL;
+       ifconn_manager->template_cnt = 0;
+#endif
+       for (i = IFCONN_NOTIFY_USB; i <= IFCONN_NOTIFY_USB_DP; i++) {
+               ret = ifconn_notifier_register(&ifconn_manager->nb,
+                                              ifconn_manager_isr,
+                                              IFCONN_NOTIFY_MANAGER, i);
+       }
+
+       return 0;
+
+ERROR_PDATA_FREE:
+       kfree(pdata);
+ERROR_IFCONN_FREE:
+       kfree(ifconn_manager);
+
+       return ret;
+}
+
+static int ifconn_manager_remove(struct platform_device *pdev)
+{
+       struct ifconn_manager_data *ifconn_manager = platform_get_drvdata(pdev);
+
+       mutex_destroy(&ifconn_manager->noti_mutex);
+       return 0;
+}
+
+static void ifconn_manager_shutdown(struct device *dev)
+{
+}
+
+#ifdef CONFIG_OF
+static struct of_device_id ifconn_manager_dt_ids[] = {
+       {.compatible = "samsung,ifconn"},
+       {}
+};
+
+MODULE_DEVICE_TABLE(of, ifconn_manager_dt_ids);
+#endif /* CONFIG_OF */
+
+static struct platform_driver ifconn_manager_driver = {
+       .driver = {
+                  .name = "ifconn",
+                  .owner = THIS_MODULE,
+                  .shutdown = ifconn_manager_shutdown,
+#ifdef CONFIG_OF
+                  .of_match_table = ifconn_manager_dt_ids,
+#endif
+                  },
+       .probe = ifconn_manager_probe,
+       .remove = ifconn_manager_remove,
+};
+
+static int __init ifconn_manager_init(void)
+{
+       return platform_driver_register(&ifconn_manager_driver);
+}
+
+static void __exit ifconn_manager_exit(void)
+{
+       platform_driver_unregister(&ifconn_manager_driver);
+}
+
+late_initcall(ifconn_manager_init);
+module_exit(ifconn_manager_exit);
+
+MODULE_DESCRIPTION("ifconn manager");
+MODULE_AUTHOR("Samsung Electronics");
+MODULE_LICENSE("GPL");
diff --git a/drivers/ifconn/ifconn_notifier.c b/drivers/ifconn/ifconn_notifier.c
new file mode 100644 (file)
index 0000000..3fdebd5
--- /dev/null
@@ -0,0 +1,184 @@
+#include <linux/device.h>
+#include <linux/module.h>
+
+#include <linux/notifier.h>
+#include <linux/ifconn/ifconn_notifier.h>
+#include <linux/ifconn/ifconn_manager.h>
+
+#define DEBUG
+#define SET_IFCONN_NOTIFIER_BLOCK(nb, fn, dev) do {    \
+               (nb)->notifier_call = (fn);             \
+               (nb)->priority = (dev);                 \
+} while (0)
+
+#define DESTROY_IFCONN_NOTIFIER_BLOCK(nb)                      \
+               SET_IFCONN_NOTIFIER_BLOCK(nb, NULL, -1)
+
+static char IFCONN_NOTI_DEV_Print[IFCONN_NOTIFY_MAX][10] = {
+       {"MANAGER"},
+       {"USB"},
+       {"BATTERY"},
+       {"PDIC"},
+       {"MUIC"},
+       {"VBUS"},
+       {"CCIC"},
+       {"DP"},
+       {"DPUSB"},
+       {"ALL"},
+};
+
+static struct ifconn_notifier pnoti[IFCONN_NOTIFY_MAX];
+
+void _ifconn_show_attr(struct ifconn_notifier_template *t)
+{
+       if (t == NULL)
+               return;
+
+       pr_info("%s, src:%s, dest:%s, id:%d, event:%d, data:0x%p\n",
+               __func__, IFCONN_NOTI_DEV_Print[t->src], IFCONN_NOTI_DEV_Print[t->dest], t->id, t->event, t->data);
+}
+
+int ifconn_notifier_register(struct notifier_block *nb, notifier_fn_t notifier,
+                            ifconn_notifier_t listener, ifconn_notifier_t src)
+{
+       int ret = 0;
+       struct ifconn_notifier_template *template = NULL;
+
+       pr_info("%s: src : %d =? listner : %d, nb : %p  register\n", __func__,
+               src, listener, nb);
+
+       if (src >= IFCONN_NOTIFY_ALL || src < 0
+           || listener >= IFCONN_NOTIFY_ALL || listener < 0) {
+               pr_err("%s: dev index err\n", __func__);
+               return -1;
+       }
+
+       SET_IFCONN_NOTIFIER_BLOCK(nb, notifier, listener);
+       ret = blocking_notifier_chain_register(&(pnoti[src].notifier_call_chain), nb);
+       if (ret < 0)
+               pr_err("%s: blocking_notifier_chain_register error(%d)\n",
+                      __func__, ret);
+
+       pnoti[src].nb[listener] = nb;
+       if (pnoti[src].sent[listener] || pnoti[src].sent[IFCONN_NOTIFY_ALL]) {
+               pr_info("%s: src : %d, listner : %d, sent : %d, sent all : %d\n", __func__,
+                       src, listener, pnoti[src].sent[listener], pnoti[src].sent[IFCONN_NOTIFY_ALL]);
+
+               if (!pnoti[src].sent[listener] && pnoti[src].sent[IFCONN_NOTIFY_ALL]) {
+                       template = &pnoti[src].ifconn_template[IFCONN_NOTIFY_ALL];
+                       _ifconn_show_attr(template);
+                       template->src = src;
+                       template->dest = listener;
+               } else {
+                       template = &(pnoti[src].ifconn_template[listener]);
+               }
+               _ifconn_show_attr(template);
+               ret = nb->notifier_call(nb,     template->id, template);
+       }
+
+       return ret;
+}
+
+int ifconn_notifier_unregister(ifconn_notifier_t src,
+                              ifconn_notifier_t listener)
+{
+       int ret = 0;
+       struct notifier_block *nb = NULL;
+
+       if (src >= IFCONN_NOTIFY_ALL || src < 0
+           || listener >= IFCONN_NOTIFY_ALL || listener < 0) {
+               pr_err("%s: dev index err\n", __func__);
+               return -1;
+       }
+       nb = pnoti[src].nb[listener];
+
+       pr_info("%s: listener=%d unregister\n", __func__, nb->priority);
+
+       ret = blocking_notifier_chain_unregister(&(pnoti[src].notifier_call_chain), nb);
+       if (ret < 0)
+               pr_err("%s: blocking_notifier_chain_unregister error(%d)\n",
+                      __func__, ret);
+       DESTROY_IFCONN_NOTIFIER_BLOCK(nb);
+
+       return ret;
+}
+
+int ifconn_notifier_notify(ifconn_notifier_t src,
+                          ifconn_notifier_t listener,
+                          int noti_id,
+                          int event,
+                          ifconn_notifier_data_param_t param_type, void *data)
+{
+       int ret = 0;
+       struct ifconn_notifier_template *template = NULL;
+       struct notifier_block *nb = NULL;
+
+       pr_info("%s: enter\n", __func__);
+
+       if (src > IFCONN_NOTIFY_ALL || src < 0
+           || listener > IFCONN_NOTIFY_ALL || listener < 0) {
+               pr_err("%s: dev index err\n", __func__);
+               return -1;
+       }
+       pr_info("%s: src = %s, listener = %s, nb = 0x%p notify\n", __func__,
+               IFCONN_NOTI_DEV_Print[src], IFCONN_NOTI_DEV_Print[listener],
+               pnoti[src].nb[listener]);
+
+       if (param_type == IFCONN_NOTIFY_PARAM_TEMPLATE) {
+               template = (struct ifconn_notifier_template *)data;
+       } else {
+               template = &(pnoti[src].ifconn_template[listener]);
+               template->id = (uint64_t) noti_id;
+               template->up_src = template->src = (uint64_t) src;
+               template->dest = (uint64_t) listener;
+               template->cable_type = template->event = event;
+               template->data = data;
+       }
+
+       _ifconn_show_attr(template);
+
+       if (pnoti[src].nb[listener] == NULL)
+               pnoti[src].sent[listener] = true;
+
+       if (listener == IFCONN_NOTIFY_ALL) {
+               ret = blocking_notifier_call_chain(&(pnoti[src].notifier_call_chain),
+                               (unsigned long)noti_id, template);
+       } else {
+               if (pnoti[src].nb[listener] == NULL) {
+                       if (param_type == IFCONN_NOTIFY_PARAM_TEMPLATE) {
+                               memcpy(&(pnoti[src].ifconn_template[listener]), template,
+                                          sizeof(struct ifconn_notifier_template));
+                       }
+                       return -1;
+               }
+
+               nb = pnoti[src].nb[listener];
+               ret = nb->notifier_call(nb, (unsigned long)noti_id, template);
+       }
+
+       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 ifconn_notifier_init(void)
+{
+       int ret = 0;
+
+       pr_info("%s\n", __func__);
+       return ret;
+}
+
+device_initcall(ifconn_notifier_init);
diff --git a/drivers/muic/Kconfig b/drivers/muic/Kconfig
new file mode 100644 (file)
index 0000000..2c52837
--- /dev/null
@@ -0,0 +1,202 @@
+#
+# MUIC devices
+#
+
+comment "MUIC configs"
+
+config USE_MUIC
+       bool "Using MUIC device driver"
+       depends on I2C
+       default n
+       help
+         If you say yes here you will get support for
+         the MUIC device driver.
+
+config USE_SAFEOUT
+       bool "Using SAFEOUT device driver"
+       depends on I2C
+       default n
+       help
+         If you say yes here you will get support for
+         the SAFEOUT device driver.
+
+config MUIC_NOTIFIER
+       bool "MUIC notifier support"
+       depends on USE_MUIC
+       default n
+       help
+         If you say yes here you will get support for
+         the MUIC attached device status change notification.
+
+config MUIC_MAX77705
+        bool "Using MAX77705 MUIC"
+        depends on USE_MUIC
+        default n
+        help
+          If you say yes here you will get support for
+          the MAX77705 MUIC chip.
+         To enable this driver,
+         USE_MUIC should be enabled.
+
+config HV_MUIC_MAX77705_AFC
+        bool "Using MAX77705 AFC MUIC"
+        depends on MUIC_MAX77705
+        default n
+        help
+          If you say yes here you will get support for
+         the MAX77705 AFC MUIC.
+         To enable this driver,
+         MUIC_MAX77705 should be enabled.
+
+config MUIC_UNIVERSAL
+       bool "UNIVERSAL MUIC"
+       depends on USE_MUIC
+       default n
+       help
+         If you say yes here you will get support for various MUIC chips.
+
+config MUIC_UNIVERSAL_MAX77854
+       bool "MAX77854 MUIC"
+       depends on MUIC_UNIVERSAL
+       default n
+       help
+         If you say yes here you will get support for the MAX77854 MUIC chip.
+
+config MUIC_UNIVERSAL_MAX77865
+       bool "MAX77865 MUIC"
+       depends on MUIC_UNIVERSAL
+       default n
+       help
+         If you say yes here you will get support for the MAX77865 MUIC chip.
+
+config MUIC_MANAGER
+       bool "MUIC Manager supports CCIC chip interface"
+       depends on USE_MUIC && MFD_S2MU004
+       default n
+       help
+         If you say yes here you will get support manager for the CCIC chip.
+
+config MUIC_HV
+       bool "MUIC_HV"
+       depends on MUIC_UNIVERSAL
+       default n
+       help
+         If you say yes here you will get support for the AFC.
+
+config MUIC_HV_12V
+       bool "MUIC_HV_12V"
+       depends on MUIC_HV
+       default n
+       help
+         If you say yes here you will get support for the AFC.
+
+config MUIC_HV_MAX77854
+       bool "MUIC_HV_MAX77854"
+       depends on MUIC_UNIVERSAL_MAX77854
+       default n
+       help
+         If you say yes here you will get support for the AFC.
+
+config MUIC_HV_MAX77865
+       bool "MUIC_HV_MAX77865"
+       depends on MUIC_UNIVERSAL_MAX77865
+       default n
+       help
+         If you say yes here you will get support for the AFC.
+
+config MUIC_SUPPORT_CCIC
+       bool "MUIC supports CCIC chip interface"
+       default n
+       help
+         If you say yes here you will get support for the CCIC chip.
+
+config MUIC_MAX77705_CCIC
+        bool "Using MAX77705 MUIC supports CCIC chip interface"
+        depends on MUIC_MAX77705 && MUIC_SUPPORT_CCIC
+        default n
+        help
+          If you say yes here you will get support for
+         the CCIC chip with MAX77705 MUIC.
+         To enable this driver,
+         MUIC_MAX77705 and MUIC_SUPPORT_CCIC should be enabled.
+
+config MUIC_TEST_FUNC
+       bool "MUIC supports several function's in TEST"
+       depends on SEC_DEBUG && !SAMSUNG_PRODUCT_SHIP
+       default n
+       help
+         If you say yes here you will get support for the TEST function's.
+
+config HICCUP_CHARGER
+       bool "Using HICCUP charger"
+       depends on USE_MUIC
+       default n
+       help
+               If you say yes here you will get support for
+               for the hiccup charger feature.
+
+config MUIC_S2MU004
+       bool "Using S2MU004 MUIC"
+       depends on USE_MUIC && MFD_S2MU004
+       default n
+       help
+         If you say yes here you will get support for the S2MU004 MUIC chip.
+
+config MUIC_S2MU004_HV
+       bool "Using S2MU004 MUIC High Voltage Charging"
+       depends on MUIC_S2MU004
+       default n
+       help
+         If you say yes here you will get support for
+         the HV voltage control functionality.
+          It will support afc and qc,
+          to implement this config.
+
+config MUIC_S2MU004_ENABLE_AUTOSW
+       bool "Using S2MU004 ENABLE AUTOSW"
+       depends on MUIC_S2MU004
+       default n
+       help
+         If you say yes here you will get support for the AUTO switch path.
+
+config MUIC_S2MU004_RID
+       bool "Using S2MU004 MUIC RID"
+       depends on MUIC_S2MU004
+       default n
+       help
+         If you say yes here,
+          you will get support for MUIC RID.
+          Usually it's used for the JIG Factory ID,
+          and the accessory.
+
+config HV_MUIC_VOLTAGE_CTRL
+       bool "Using AFC Voltage control functionality"
+       depends on MUIC_HV_S2MU004_AFC
+       default n
+       help
+         If you say yes here you will get support for
+         the AFC voltage control functionality.
+
+config MUIC_S2MU005
+       bool "Using S2MU005 MUIC"
+       depends on USE_MUIC && MFD_S2MU005
+       default n
+       help
+         If you say yes here you will get support for
+         for the S2MU005 MUIC chip.
+
+config MUIC_S2MU005_ENABLE_AUTOSW
+       bool "Using S2MU005 MUIC ENABLE AUTOSW"
+       depends on MUIC_S2MU005
+       default n
+       help
+         If you say yes here you will get support for
+         for the S2MU005 MUIC chip ENABLE AUTOSW feature.
+
+config MUIC_S2MU005_DISCHARGING_WA
+       bool "Using S2MU005 MUIC DISCHARGING WORKAROUND"
+       depends on MUIC_S2MU005
+       default n
+       help
+               If you say yes here you will get support for
+               for the S2MU005 MUIC chip DISCHARGING WA feature.
diff --git a/drivers/muic/Makefile b/drivers/muic/Makefile
new file mode 100644 (file)
index 0000000..593b7b8
--- /dev/null
@@ -0,0 +1,44 @@
+#
+# Makefile for muic devices
+#
+
+ifndef CONFIG_MUIC_S2MU004
+obj-y                                  += muic-core.o
+
+obj-$(CONFIG_MUIC_NOTIFIER)            += muic_notifier.o
+endif
+obj-$(CONFIG_MUIC_MAX77854)            += max77854-muic.o
+obj-$(CONFIG_HV_MUIC_MAX77854_AFC)     += max77854-muic-afc.o
+obj-$(CONFIG_MUIC_MAX77705)            += max77705-muic.o
+obj-$(CONFIG_HV_MUIC_MAX77705_AFC)     += max77705-muic-afc.o
+obj-$(CONFIG_MUIC_MAX77705_CCIC)       += max77705-muic-ccic.o
+
+# MUIC_UNIVERSAL
+ifeq ($(CONFIG_MUIC_UNIVERSAL), y)
+  obj-$(CONFIG_MUIC_SUPPORT_CCIC)      += universal/muic_ccic.o
+endif
+obj-$(CONFIG_MUIC_UNIVERSAL)   += universal/muic_usb.o
+obj-$(CONFIG_MUIC_UNIVERSAL)   += universal/muic_coagent.o
+#obj-$(CONFIG_MUIC_UNIVERSAL)  += universal/muic_task.o
+obj-$(CONFIG_MUIC_UNIVERSAL)   += universal/muic_state.o
+obj-$(CONFIG_MUIC_UNIVERSAL)   += universal/muic_apis.o
+obj-$(CONFIG_MUIC_UNIVERSAL)   += universal/muic_sysfs.o
+obj-$(CONFIG_MUIC_UNIVERSAL)   += universal/muic_debug.o
+obj-$(CONFIG_MUIC_UNIVERSAL)   += universal/muic_dt.o
+obj-$(CONFIG_MUIC_UNIVERSAL)   += universal/muic_i2c.o
+obj-$(CONFIG_MUIC_UNIVERSAL)   += universal/muic_regmap.o
+obj-$(CONFIG_MUIC_UNIVERSAL)   += universal/muic_vps.o
+obj-$(CONFIG_MUIC_UNIVERSAL_MAX77854)  += universal/muic_task_max77854.o
+obj-$(CONFIG_MUIC_UNIVERSAL_MAX77854)  += universal/muic_regmap_max77854.o
+obj-$(CONFIG_MUIC_HV_MAX77854)         += universal/muic_hv_max77854.o
+obj-$(CONFIG_MUIC_UNIVERSAL_MAX77865)  += universal/muic_task_max77865.o
+obj-$(CONFIG_MUIC_UNIVERSAL_MAX77865)  += universal/muic_regmap_max77865.o
+obj-$(CONFIG_MUIC_HV_MAX77865)         += universal/muic_hv_max77865.o
+
+obj-$(CONFIG_MUIC_MANAGER)             += muic_manager.o
+obj-$(CONFIG_MUIC_S2MU004)             += muic_core.o s2mu004-muic.o s2mu004-muic-sysfs.o
+obj-$(CONFIG_MUIC_S2MU004_HV)  += s2mu004-muic-hv.o
+obj-$(CONFIG_MUIC_S2MU005)             += s2mu005-muic.o
+ifdef CONFIG_MUIC_S2MU004
+obj-$(CONFIG_MUIC_NOTIFIER)            += s2mu004-muic-notifier.o
+endif
diff --git a/drivers/muic/muic-core.c b/drivers/muic/muic-core.c
new file mode 100644 (file)
index 0000000..731d193
--- /dev/null
@@ -0,0 +1,533 @@
+/* drivers/muic/muic-core.c
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/i2c-gpio.h>
+#include <linux/gpio.h>
+
+/* switch device header */
+#ifdef CONFIG_ANDROID_SWITCH
+#include "../staging/android/switch/switch.h"
+#endif /* CONFIG_ANDROID_SWITCH */
+
+#if defined(CONFIG_USE_SAFEOUT)
+#include <linux/regulator/consumer.h>
+#endif
+
+#if defined(CONFIG_USB_HW_PARAM)
+#include <linux/usb_notify.h>
+#endif
+
+#if defined(CONFIG_MFD_MAX77804) || defined(CONFIG_MFD_MAX77804K)
+#include <linux/mfd/max77804.h>
+#include <linux/mfd/max77804-private.h>
+#elif defined(CONFIG_MFD_MAX77828)
+#include <linux/mfd/max77828.h>
+#include <linux/mfd/max77828-private.h>
+#elif defined(CONFIG_MFD_MAX77843)
+#include <linux/mfd/max77843.h>
+#include <linux/mfd/max77843-private.h>
+#elif defined(CONFIG_MFD_MAX77833)
+#include <linux/mfd/max77833.h>
+#include <linux/mfd/max77833-private.h>
+#elif defined(CONFIG_MFD_MAX77888)
+#include <linux/mfd/max77888.h>
+#include <linux/mfd/max77888-private.h>
+#endif
+
+#include <linux/muic/muic.h>
+
+#if defined(CONFIG_MUIC_NOTIFIER)
+#include <linux/muic/muic_notifier.h>
+#endif /* CONFIG_MUIC_NOTIFIER */
+
+#if defined(CONFIG_MUIC_SUPPORT_CCIC) && defined(CONFIG_CCIC_NOTIFIER)
+#include <linux/ccic/ccic_notifier.h>
+#endif
+
+#ifdef CONFIG_ANDROID_SWITCH
+static struct switch_dev switch_dock = {
+       .name = "dock",
+};
+
+struct switch_dev switch_uart3 = {
+       .name = "uart3", /* sys/class/switch/uart3/state */
+};
+
+#ifdef CONFIG_SEC_FACTORY
+struct switch_dev switch_attached_muic_cable = {
+       .name = "attached_muic_cable", /* sys/class/switch/attached_muic_cable/state */
+};
+#endif
+#endif /* CONFIG_ANDROID_SWITCH */
+
+#if defined(CONFIG_MUIC_NOTIFIER)
+static struct notifier_block dock_notifier_block;
+static struct notifier_block cable_data_notifier_block;
+
+void muic_send_dock_intent(int type)
+{
+       pr_info("%s: MUIC dock type(%d)\n", __func__, type);
+#ifdef CONFIG_ANDROID_SWITCH
+       switch_set_state(&switch_dock, type);
+#endif
+}
+
+#ifdef CONFIG_SEC_FACTORY
+void muic_send_attached_muic_cable_intent(int type)
+{
+       pr_info("%s: MUIC attached_muic_cable type(%d)\n", __func__, type);
+#ifdef CONFIG_ANDROID_SWITCH
+       switch_set_state(&switch_attached_muic_cable, type);
+#endif
+}
+#endif
+
+static int muic_dock_attach_notify(int type, const char *name)
+{
+       pr_info("%s: %s\n", __func__, name);
+       muic_send_dock_intent(type);
+
+       return NOTIFY_OK;
+}
+
+static int muic_dock_detach_notify(void)
+{
+       pr_info("%s\n", __func__);
+       muic_send_dock_intent(MUIC_DOCK_DETACHED);
+
+       return NOTIFY_OK;
+}
+
+static int muic_handle_dock_notification(struct notifier_block *nb,
+                       unsigned long action, void *data)
+{
+#if defined(CONFIG_CCIC_NOTIFIER) && defined(CONFIG_MUIC_SUPPORT_CCIC)
+       CC_NOTI_ATTACH_TYPEDEF *pnoti = (CC_NOTI_ATTACH_TYPEDEF *)data;
+       muic_attached_dev_t attached_dev = pnoti->cable_type;
+#else
+       muic_attached_dev_t attached_dev = *(muic_attached_dev_t *)data;
+#endif
+       int type = MUIC_DOCK_DETACHED;
+       const char *name;
+
+       switch (attached_dev) {
+       case ATTACHED_DEV_DESKDOCK_MUIC:
+       case ATTACHED_DEV_DESKDOCK_VB_MUIC:
+               if (action == MUIC_NOTIFY_CMD_ATTACH) {
+                       type = MUIC_DOCK_DESKDOCK;
+                       name = "Desk Dock Attach";
+                       return muic_dock_attach_notify(type, name);
+               }
+               else if (action == MUIC_NOTIFY_CMD_DETACH)
+                       return muic_dock_detach_notify();
+               break;
+       case ATTACHED_DEV_CARDOCK_MUIC:
+               if (action == MUIC_NOTIFY_CMD_ATTACH) {
+                       type = MUIC_DOCK_CARDOCK;
+                       name = "Car Dock Attach";
+                       return muic_dock_attach_notify(type, name);
+               }
+               else if (action == MUIC_NOTIFY_CMD_DETACH)
+                       return muic_dock_detach_notify();
+               break;
+       case ATTACHED_DEV_SMARTDOCK_MUIC:
+       case ATTACHED_DEV_SMARTDOCK_VB_MUIC:
+       case ATTACHED_DEV_SMARTDOCK_TA_MUIC:
+       case ATTACHED_DEV_SMARTDOCK_USB_MUIC:
+               if (action == MUIC_NOTIFY_CMD_LOGICALLY_ATTACH) {
+                       type = MUIC_DOCK_SMARTDOCK;
+                       name = "Smart Dock Attach";
+                       return muic_dock_attach_notify(type, name);
+               }
+               else if (action == MUIC_NOTIFY_CMD_LOGICALLY_DETACH)
+                       return muic_dock_detach_notify();
+               break;
+       case ATTACHED_DEV_UNIVERSAL_MMDOCK_MUIC:
+               if (action == MUIC_NOTIFY_CMD_ATTACH) {
+                       type = MUIC_DOCK_SMARTDOCK;
+                       name = "Universal MMDock Attach";
+                       return muic_dock_attach_notify(type, name);
+               }
+               else if (action == MUIC_NOTIFY_CMD_DETACH)
+                       return muic_dock_detach_notify();
+               break;
+       case ATTACHED_DEV_AUDIODOCK_MUIC:
+               if (action == MUIC_NOTIFY_CMD_ATTACH) {
+                       type = MUIC_DOCK_AUDIODOCK;
+                       name = "Audio Dock Attach";
+                       return muic_dock_attach_notify(type, name);
+               }
+               else if (action == MUIC_NOTIFY_CMD_DETACH)
+                       return muic_dock_detach_notify();
+               break;
+       case ATTACHED_DEV_HMT_MUIC:
+               if (action == MUIC_NOTIFY_CMD_ATTACH) {
+                       type = MUIC_DOCK_HMT;
+                       name = "HMT Attach";
+                       return muic_dock_attach_notify(type, name);
+               }
+               else if (action == MUIC_NOTIFY_CMD_DETACH)
+                       return muic_dock_detach_notify();
+               break;
+       case ATTACHED_DEV_GAMEPAD_MUIC:
+               if (action == MUIC_NOTIFY_CMD_ATTACH) {
+                       type = MUIC_DOCK_GAMEPAD;
+                       name = "Gamepad Attach";
+                       return muic_dock_attach_notify(type, name);
+               } else if (action == MUIC_NOTIFY_CMD_DETACH)
+                       return muic_dock_detach_notify();
+               break;
+       default:
+               break;
+       }
+
+       pr_info("%s: ignore(%d)\n", __func__, attached_dev);
+       return NOTIFY_DONE;
+}
+
+static int muic_handle_cable_data_notification(struct notifier_block *nb,
+                       unsigned long action, void *data)
+{
+#if defined(CONFIG_CCIC_NOTIFIER) && defined(CONFIG_MUIC_SUPPORT_CCIC)
+       CC_NOTI_ATTACH_TYPEDEF *pnoti = (CC_NOTI_ATTACH_TYPEDEF *)data;
+       muic_attached_dev_t attached_dev = pnoti->cable_type;
+#else
+       muic_attached_dev_t attached_dev = *(muic_attached_dev_t *)data;
+#endif
+       int jig_state = 0;
+#if defined(CONFIG_USB_HW_PARAM)
+       struct otg_notify *o_notify = get_otg_notify();
+#endif
+
+       switch (attached_dev) {
+       case ATTACHED_DEV_JIG_UART_OFF_MUIC:
+       case ATTACHED_DEV_JIG_UART_OFF_VB_MUIC:         /* VBUS enabled */
+       case ATTACHED_DEV_JIG_UART_OFF_VB_OTG_MUIC:     /* for otg test */
+       case ATTACHED_DEV_JIG_UART_OFF_VB_FG_MUIC:      /* for fg test */
+       case ATTACHED_DEV_JIG_UART_ON_MUIC:
+       case ATTACHED_DEV_JIG_UART_ON_VB_MUIC:          /* VBUS enabled */
+       case ATTACHED_DEV_JIG_USB_OFF_MUIC:
+       case ATTACHED_DEV_JIG_USB_ON_MUIC:
+               if (action == MUIC_NOTIFY_CMD_ATTACH)
+                       jig_state = 1;
+               break;
+#if defined(CONFIG_USB_HW_PARAM)
+       case ATTACHED_DEV_TIMEOUT_OPEN_MUIC:
+               if (action == MUIC_NOTIFY_CMD_ATTACH && o_notify)
+                       inc_hw_param(o_notify, USB_MUIC_DCD_TIMEOUT_COUNT);
+               break;
+#endif
+       default:
+               jig_state = 0;
+               break;
+       }
+
+       pr_info("%s: MUIC uart type(%d)\n", __func__, jig_state);
+#ifdef CONFIG_ANDROID_SWITCH
+       switch_set_state(&switch_uart3, jig_state);
+#endif
+       return NOTIFY_DONE;
+}
+#endif /* CONFIG_MUIC_NOTIFIER */
+
+#if defined(CONFIG_USE_SAFEOUT)
+int muic_set_safeout(int safeout_path)
+{
+       struct regulator *regulator;
+       int ret;
+
+       pr_info("%s:MUIC safeout path=%d\n", __func__, safeout_path);
+
+       if (safeout_path == MUIC_PATH_USB_CP) {
+               regulator = regulator_get(NULL, "safeout1_range");
+               if (IS_ERR(regulator) || regulator == NULL)
+                       return -ENODEV;
+
+               if (regulator_is_enabled(regulator))
+                       regulator_force_disable(regulator);
+               regulator_put(regulator);
+
+               regulator = regulator_get(NULL, "safeout2_range");
+               if (IS_ERR(regulator) || regulator == NULL)
+                       return -ENODEV;
+
+               if (!regulator_is_enabled(regulator)) {
+                       ret = regulator_enable(regulator);
+                       if (ret)
+                               goto err;
+               }
+               regulator_put(regulator);
+       } else if (safeout_path == MUIC_PATH_USB_AP) {
+               regulator = regulator_get(NULL, "safeout1_range");
+               if (IS_ERR(regulator) || regulator == NULL)
+                       return -ENODEV;
+
+               if (!regulator_is_enabled(regulator)) {
+                       ret = regulator_enable(regulator);
+                       if (ret)
+                               goto err;
+               }
+               regulator_put(regulator);
+
+               regulator = regulator_get(NULL, "safeout2_range");
+               if (IS_ERR(regulator) || regulator == NULL)
+                       return -ENODEV;
+
+               if (regulator_is_enabled(regulator))
+                       regulator_force_disable(regulator);
+               regulator_put(regulator);
+       }  else {
+               pr_info("%s: not control safeout(%d)\n", __func__, safeout_path);
+               return -EINVAL;
+       }
+
+       return 0;
+err:
+       pr_info("%s: cannot regulator_enable (%d)\n", __func__, ret);
+       regulator_put(regulator);
+       return ret;
+}
+#endif /* CONFIG_USE_SAFEOUT */
+
+static void muic_init_switch_dev_cb(void)
+{
+#ifdef CONFIG_ANDROID_SWITCH
+       int ret;
+
+       /* for DockObserver */
+       ret = switch_dev_register(&switch_dock);
+       if (ret < 0) {
+               pr_err("%s: Failed to register dock switch(%d)\n",
+                               __func__, ret);
+               return;
+       }
+
+       /* for UART event */
+       ret = switch_dev_register(&switch_uart3);
+       if (ret < 0) {
+               pr_err("%s: Failed to register uart3 switch(%d)\n",
+                               __func__, ret);
+               return;
+       }
+
+#ifdef CONFIG_SEC_FACTORY
+       /* for cable type event */
+       ret = switch_dev_register(&switch_attached_muic_cable);
+       if (ret < 0) {
+               pr_err("%s: Failed to register attached_muic_cable switch(%d)\n",
+                               __func__, ret);
+               return;
+       }
+#endif
+#endif /* CONFIG_ANDROID_SWITCH */
+
+#if defined(CONFIG_MUIC_NOTIFIER)
+       muic_notifier_register(&dock_notifier_block,
+                       muic_handle_dock_notification, MUIC_NOTIFY_DEV_DOCK);
+       muic_notifier_register(&cable_data_notifier_block,
+                       muic_handle_cable_data_notification, MUIC_NOTIFY_DEV_CABLE_DATA);
+#endif /* CONFIG_MUIC_NOTIFIER */
+
+       pr_info("%s: done\n", __func__);
+}
+
+static void muic_cleanup_switch_dev_cb(void)
+{
+#ifdef CONFIG_ANDROID_SWITCH
+#ifdef CONFIG_SEC_FACTORY
+       /* for cable type event */
+       switch_dev_unregister(&switch_attached_muic_cable);
+#endif
+       /* for UART event */
+       switch_dev_unregister(&switch_uart3);
+       /* for DockObserver */
+       switch_dev_unregister(&switch_dock);
+#endif /* CONFIG_ANDROID_SWITCH */
+#if defined(CONFIG_MUIC_NOTIFIER)
+       muic_notifier_unregister(&dock_notifier_block);
+       muic_notifier_unregister(&cable_data_notifier_block);
+#endif /* CONFIG_MUIC_NOTIFIER */
+
+       pr_info("%s: done\n", __func__);
+}
+
+extern struct muic_platform_data muic_pdata;
+
+/* func : set_switch_sel
+ * switch_sel value get from bootloader comand line
+ * switch_sel data consist 8 bits (xxxxyyyyzzzz)
+ * first 4bits(zzzz) mean path infomation.
+ * next 4bits(yyyy) mean if pmic version info
+ * next 4bits(xxxx) mean afc disable info
+ */
+static int set_switch_sel(char *str)
+{
+       get_option(&str, &muic_pdata.switch_sel);
+       muic_pdata.switch_sel = (muic_pdata.switch_sel) & 0xfff;
+       pr_info("%s: switch_sel: 0x%03x\n", __func__,
+                       muic_pdata.switch_sel);
+
+       return muic_pdata.switch_sel;
+}
+__setup("pmic_info=", set_switch_sel);
+
+int get_switch_sel(void)
+{
+       return muic_pdata.switch_sel;
+}
+
+/* afc_mode:
+ *   0x31 : Disabled
+ *   0x30 : Enabled
+ */
+static int afc_mode = 0;
+static int __init set_afc_mode(char *str)
+{
+       int mode;
+       get_option(&str, &mode);
+       afc_mode = (mode & 0x0000FF00) >> 8;
+       pr_info("%s: afc_mode is 0x%02x\n", __func__, afc_mode);
+
+       return 0;
+}
+early_param("charging_mode", set_afc_mode);
+
+int get_afc_mode(void)
+{
+       return afc_mode;
+}
+
+static int __ccic_info;
+/*
+ * __ccic_info :
+ * b'0: 1 if an active ccic is present,
+ *        0 when muic works without ccic chip or
+ *              no ccic Noti. registration is needed
+ *              even though a ccic chip is present.
+ */
+static int set_ccic_info(char *str)
+{
+       get_option(&str, &__ccic_info);
+
+       pr_info("%s: ccic_info: 0x%04x\n", __func__, __ccic_info);
+
+       return __ccic_info;
+}
+__setup("ccic_info=", set_ccic_info);
+
+int get_ccic_info(void)
+{
+       return __ccic_info;
+}
+
+bool is_muic_usb_path_ap_usb(void)
+{
+       if (MUIC_PATH_USB_AP == muic_pdata.usb_path) {
+               pr_info("%s: [%d]\n", __func__, muic_pdata.usb_path);
+               return true;
+       }
+
+       return false;
+}
+
+bool is_muic_usb_path_cp_usb(void)
+{
+       if (MUIC_PATH_USB_CP == muic_pdata.usb_path) {
+               pr_info("%s: [%d]\n", __func__, muic_pdata.usb_path);
+               return true;
+       }
+
+       return false;
+}
+
+static int muic_init_gpio_cb(int switch_sel)
+{
+       struct muic_platform_data *pdata = &muic_pdata;
+       const char *usb_mode;
+       const char *uart_mode;
+       int ret = 0;
+
+       pr_info("%s (%d)\n", __func__, switch_sel);
+
+       if (switch_sel & SWITCH_SEL_USB_MASK) {
+               pdata->usb_path = MUIC_PATH_USB_AP;
+               usb_mode = "PDA";
+       } else {
+               pdata->usb_path = MUIC_PATH_USB_CP;
+               usb_mode = "MODEM";
+       }
+
+       if (pdata->set_gpio_usb_sel)
+               ret = pdata->set_gpio_usb_sel(pdata->uart_path);
+
+       if (switch_sel & SWITCH_SEL_UART_MASK) {
+               pdata->uart_path = MUIC_PATH_UART_AP;
+               uart_mode = "AP";
+       } else {
+               pdata->uart_path = MUIC_PATH_UART_CP;
+               uart_mode = "CP";
+       }
+
+       /* These flags MUST be updated again from probe function */
+       pdata->rustproof_on = false;
+
+       pdata->afc_disable = false;
+
+       if (pdata->set_gpio_uart_sel)
+               ret = pdata->set_gpio_uart_sel(pdata->uart_path);
+
+       pr_info("%s: usb_path(%s), uart_path(%s)\n", __func__,
+                       usb_mode, uart_mode);
+
+       return ret;
+}
+
+int muic_afc_set_voltage(int voltage)
+{
+       struct muic_platform_data *pdata = &muic_pdata;
+
+       if (pdata && pdata->muic_afc_set_voltage_cb)
+               return pdata->muic_afc_set_voltage_cb(voltage);
+
+       pr_err("%s: cannot supported\n", __func__);
+       return -ENODEV;
+}
+
+int muic_hv_charger_init(void)
+{
+       struct muic_platform_data *pdata = &muic_pdata;
+
+       if (pdata && pdata->muic_hv_charger_init_cb)
+               return pdata->muic_hv_charger_init_cb();
+
+       pr_err("%s: cannot supported\n", __func__);
+       return -ENODEV;
+}
+
+int muic_set_hiccup_mode(int on_off)
+{
+       struct muic_platform_data *pdata = &muic_pdata;
+
+       if (pdata && pdata->muic_set_hiccup_mode_cb)
+               return pdata->muic_set_hiccup_mode_cb(on_off);
+
+       pr_err("%s: cannot supported\n", __func__);
+       return -ENODEV;
+}
+
+struct muic_platform_data muic_pdata = {
+       .init_switch_dev_cb     = muic_init_switch_dev_cb,
+       .cleanup_switch_dev_cb  = muic_cleanup_switch_dev_cb,
+       .init_gpio_cb           = muic_init_gpio_cb,
+#if defined(CONFIG_USE_SAFEOUT)
+       .set_safeout            = muic_set_safeout,
+#endif /* CONFIG_USE_SAFEOUT */
+};
diff --git a/drivers/muic/muic_core.c b/drivers/muic/muic_core.c
new file mode 100644 (file)
index 0000000..15ff6db
--- /dev/null
@@ -0,0 +1,1043 @@
+/* drivers/muic/muic-core.c
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+ #define pr_fmt(fmt)   "[MUIC] " fmt
+
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/i2c-gpio.h>
+#include <linux/gpio.h>
+
+/* switch device header */
+#ifdef CONFIG_SWITCH
+#include <linux/switch.h>
+#endif /* CONFIG_SWITCH */
+
+#include <linux/slab.h>
+#include <linux/muic/muic_core.h>
+#include <linux/muic/muic_interface.h>
+
+#if defined(CONFIG_MUIC_NOTIFIER)
+#include <linux/muic/s2mu004-muic-notifier.h>
+#endif /* CONFIG_MUIC_NOTIFIER */
+
+#if defined(CONFIG_CCIC_NOTIFIER)
+#include <linux/ccic/ccic_notifier.h>
+#endif
+
+#ifdef CONFIG_SWITCH
+static struct switch_dev switch_dock = {
+       .name = "dock",
+};
+#endif /* CONFIG_SWITCH */
+
+#if defined(CONFIG_MUIC_NOTIFIER)
+static struct notifier_block dock_notifier_block;
+
+void muic_send_dock_intent(int type)
+{
+       pr_info("%s: MUIC dock type(%d)\n", __func__, type);
+#ifdef CONFIG_SWITCH
+       switch_set_state(&switch_dock, type);
+#endif
+}
+
+static int muic_dock_attach_notify(int type, const char *name)
+{
+       pr_info("%s: %s\n", __func__, name);
+       muic_send_dock_intent(type);
+
+       return NOTIFY_OK;
+}
+
+static int muic_dock_detach_notify(void)
+{
+       pr_info("%s\n", __func__);
+       muic_send_dock_intent(MUIC_DOCK_DETACHED);
+
+       return NOTIFY_OK;
+}
+
+static int muic_handle_dock_notification(struct notifier_block *nb,
+                       unsigned long action, void *data)
+{
+#if defined(CONFIG_CCIC_NOTIFIER)
+       CC_NOTI_ATTACH_TYPEDEF *pnoti = (CC_NOTI_ATTACH_TYPEDEF *)data;
+       muic_attached_dev_t attached_dev = pnoti->cable_type;
+#else
+       muic_attached_dev_t attached_dev = *(muic_attached_dev_t *)data;
+#endif
+       int type = MUIC_DOCK_DETACHED;
+       const char *name;
+
+       switch (attached_dev) {
+       case ATTACHED_DEV_DESKDOCK_MUIC:
+       case ATTACHED_DEV_DESKDOCK_VB_MUIC:
+#if defined(CONFIG_SEC_FACTORY)
+       case ATTACHED_DEV_JIG_UART_ON_MUIC:
+#endif
+               if (action == MUIC_NOTIFY_CMD_ATTACH) {
+                       type = MUIC_DOCK_DESKDOCK;
+                       name = "Desk Dock Attach";
+                       return muic_dock_attach_notify(type, name);
+               } else if (action == MUIC_NOTIFY_CMD_DETACH)
+                       return muic_dock_detach_notify();
+               break;
+       case ATTACHED_DEV_CARDOCK_MUIC:
+               if (action == MUIC_NOTIFY_CMD_ATTACH) {
+                       type = MUIC_DOCK_CARDOCK;
+                       name = "Car Dock Attach";
+                       return muic_dock_attach_notify(type, name);
+               } else if (action == MUIC_NOTIFY_CMD_DETACH)
+                       return muic_dock_detach_notify();
+               break;
+       case ATTACHED_DEV_SMARTDOCK_MUIC:
+       case ATTACHED_DEV_SMARTDOCK_VB_MUIC:
+       case ATTACHED_DEV_SMARTDOCK_TA_MUIC:
+       case ATTACHED_DEV_SMARTDOCK_USB_MUIC:
+               if (action == MUIC_NOTIFY_CMD_LOGICALLY_ATTACH) {
+                       type = MUIC_DOCK_SMARTDOCK;
+                       name = "Smart Dock Attach";
+                       return muic_dock_attach_notify(type, name);
+               } else if (action == MUIC_NOTIFY_CMD_LOGICALLY_DETACH)
+                       return muic_dock_detach_notify();
+               break;
+       case ATTACHED_DEV_UNIVERSAL_MMDOCK_MUIC:
+               if (action == MUIC_NOTIFY_CMD_ATTACH) {
+                       type = MUIC_DOCK_SMARTDOCK;
+                       name = "Universal MMDock Attach";
+                       return muic_dock_attach_notify(type, name);
+               } else if (action == MUIC_NOTIFY_CMD_DETACH)
+                       return muic_dock_detach_notify();
+               break;
+       case ATTACHED_DEV_AUDIODOCK_MUIC:
+               if (action == MUIC_NOTIFY_CMD_ATTACH) {
+                       type = MUIC_DOCK_AUDIODOCK;
+                       name = "Audio Dock Attach";
+                       return muic_dock_attach_notify(type, name);
+               } else if (action == MUIC_NOTIFY_CMD_DETACH)
+                       return muic_dock_detach_notify();
+               break;
+       case ATTACHED_DEV_HMT_MUIC:
+               if (action == MUIC_NOTIFY_CMD_ATTACH) {
+                       type = MUIC_DOCK_HMT;
+                       name = "HMT Attach";
+                       return muic_dock_attach_notify(type, name);
+               } else if (action == MUIC_NOTIFY_CMD_DETACH)
+                       return muic_dock_detach_notify();
+               break;
+       case ATTACHED_DEV_GAMEPAD_MUIC:
+               if (action == MUIC_NOTIFY_CMD_ATTACH) {
+                       type = MUIC_DOCK_GAMEPAD;
+                       name = "Gamepad Attach";
+                       return muic_dock_attach_notify(type, name);
+               } else if (action == MUIC_NOTIFY_CMD_DETACH)
+                       return muic_dock_detach_notify();
+               break;
+       default:
+               break;
+       }
+
+       pr_info("%s: ignore(%d)\n", __func__, attached_dev);
+       return NOTIFY_DONE;
+}
+#endif /* CONFIG_MUIC_NOTIFIER */
+
+static void muic_init_switch_dev_cb(void)
+{
+#ifdef CONFIG_SWITCH
+       int ret;
+
+       /* for DockObserver */
+       ret = switch_dev_register(&switch_dock);
+       if (ret < 0) {
+               pr_err("%s: Failed to register dock switch(%d)\n",
+                               __func__, ret);
+               return;
+       }
+#endif /* CONFIG_SWITCH */
+
+#if defined(CONFIG_MUIC_NOTIFIER)
+       muic_notifier_register(&dock_notifier_block,
+                       muic_handle_dock_notification, MUIC_NOTIFY_DEV_DOCK);
+#endif /* CONFIG_MUIC_NOTIFIER */
+
+       pr_info("%s: done\n", __func__);
+}
+
+static void muic_cleanup_switch_dev_cb(void)
+{
+#if defined(CONFIG_MUIC_NOTIFIER)
+       muic_notifier_unregister(&dock_notifier_block);
+#endif /* CONFIG_MUIC_NOTIFIER */
+
+       pr_info("%s: done\n", __func__);
+}
+
+static int switch_sel;
+/* func : set_switch_sel
+ * switch_sel value get from bootloader comand line
+ * switch_sel data consist 8 bits (xxxxyyyyzzzz)
+ * first 4bits(zzzz) mean path infomation.
+ * next 4bits(yyyy) mean if pmic version info
+ * next 4bits(xxxx) mean afc disable info
+ */
+static int set_switch_sel(char *str)
+{
+       get_option(&str, &switch_sel);
+       switch_sel &= 0xfff;
+       pr_info("%s: switch_sel: 0x%03x\n", __func__,
+                       switch_sel);
+       return switch_sel;
+}
+__setup("pmic_info=", set_switch_sel);
+
+int get_switch_sel(void)
+{
+       return switch_sel;
+}
+
+/* afc_mode:
+ *   0x31 : Disabled
+ *   0x30 : Enabled
+ */
+static int afc_mode;
+static int __init set_afc_mode(char *str)
+{
+       int mode;
+
+       get_option(&str, &mode);
+       afc_mode = (mode & 0x0000FF00) >> 8;
+       pr_info("%s: afc_mode is 0x%02x\n", __func__, afc_mode);
+
+       return 0;
+}
+early_param("charging_mode", set_afc_mode);
+
+int get_afc_mode(void)
+{
+       return afc_mode;
+}
+
+int muic_init_gpio_cb(void *data, int switch_sel)
+{
+       struct muic_platform_data *muic_pdata = (struct muic_platform_data *)data;
+       const char *usb_mode;
+       const char *uart_mode;
+       struct muic_interface_t *muic_if = (struct muic_interface_t *)muic_pdata->muic_if;
+       int ret = 0;
+
+       pr_info("%s (%d)\n", __func__, switch_sel);
+
+       if (switch_sel & SWITCH_SEL_USB_MASK) {
+               muic_pdata->usb_path = MUIC_PATH_USB_AP;
+               usb_mode = "PDA";
+       } else {
+               muic_pdata->usb_path = MUIC_PATH_USB_CP;
+               usb_mode = "MODEM";
+       }
+
+       MUIC_PDATA_FUNC_MULTI_PARAM(muic_if->set_gpio_usb_sel,
+               muic_pdata->drv_data, muic_pdata->uart_path, &ret);
+
+       if (switch_sel & SWITCH_SEL_UART_MASK) {
+               muic_pdata->uart_path = MUIC_PATH_UART_AP;
+               uart_mode = "AP";
+       } else {
+               muic_pdata->uart_path = MUIC_PATH_UART_CP;
+               uart_mode = "CP";
+       }
+
+       /* These flags MUST be updated again from probe function */
+       muic_pdata->rustproof_on = false;
+
+       muic_pdata->afc_disable = false;
+
+       MUIC_PDATA_FUNC_MULTI_PARAM(muic_if->set_gpio_uart_sel,
+               muic_pdata->drv_data, muic_pdata->uart_path, &ret);
+
+       pr_info("%s: usb_path(%s), uart_path(%s)\n", __func__,
+                       usb_mode, uart_mode);
+
+       return ret;
+}
+
+static int muic_core_switch_to_dock_audio(struct muic_platform_data *muic_pdata)
+{
+       int ret = 0;
+       struct muic_interface_t *muic_if = (struct muic_interface_t *)muic_pdata->muic_if;
+
+       pr_info("%s\n", __func__);
+
+       if (muic_if->set_com_to_audio == NULL) {
+               pr_err("%s func not set.\n", __func__);
+               return -1;
+       }
+
+       MUIC_PDATA_FUNC(muic_if->set_com_to_audio, muic_pdata->drv_data, &ret);
+       if (ret) {
+               pr_err("%s com->audio set err\n", __func__);
+               return ret;
+       }
+
+       return ret;
+}
+
+static int muic_core_switch_to_system_audio(struct muic_platform_data *muic_pdata)
+{
+       int ret = 0;
+
+       pr_info("%s\n", __func__);
+
+       return ret;
+}
+
+static int muic_core_switch_to_usb(struct muic_platform_data *muic_pdata, int path)
+{
+       int ret = 0;
+       struct muic_interface_t *muic_if = (struct muic_interface_t *)muic_pdata->muic_if;
+
+       pr_info("%s\n", __func__);
+#if !defined(CONFIG_MUIC_USB_SWITCH)
+       if (muic_pdata->gpio_usb_sel) {
+               MUIC_PDATA_FUNC_MULTI_PARAM(muic_if->set_gpio_usb_sel,
+                       muic_pdata->drv_data, path, &ret);
+               if (ret) {
+                       pr_err("%s com->uart set err\n", __func__);
+                       return ret;
+               }
+       }
+#endif
+
+       MUIC_PDATA_FUNC(muic_if->set_switch_to_usb, muic_pdata->drv_data, &ret);
+       if (ret) {
+               pr_err("%s com->usb set err\n", __func__);
+               return ret;
+       }
+
+       return ret;
+}
+
+static int muic_core_switch_to_uart(struct muic_platform_data *muic_pdata, int path)
+{
+       int ret = 0;
+       struct muic_interface_t *muic_if = (struct muic_interface_t *)muic_pdata->muic_if;
+
+       pr_info("%s\n", __func__);
+#if !defined(CONFIG_MUIC_UART_SWITCH)
+       if (muic_pdata->gpio_uart_sel) {
+               MUIC_PDATA_FUNC_MULTI_PARAM(muic_if->set_gpio_uart_sel,
+                       muic_pdata->drv_data, path, &ret);
+               if (ret) {
+                       pr_err("%s com->uart set err\n", __func__);
+                       return ret;
+               }
+       }
+#endif
+
+       MUIC_PDATA_FUNC(muic_if->set_switch_to_uart, muic_pdata->drv_data, &ret);
+       if (ret) {
+               pr_err("%s com->uart set err\n", __func__);
+               return ret;
+       }
+
+       return ret;
+}
+
+static int muic_core_attach_charger(struct muic_platform_data *muic_pdata,
+                       muic_attached_dev_t new_dev)
+{
+       int ret = 0;
+
+       pr_info("%s : %d\n", __func__, new_dev);
+
+       muic_pdata->attached_dev = new_dev;
+
+       return ret;
+}
+
+static int muic_core_detach_charger(struct muic_platform_data *muic_pdata)
+{
+       int ret = 0;
+       struct muic_interface_t *muic_if = (struct muic_interface_t *)muic_pdata->muic_if;
+
+       pr_info("%s\n", __func__);
+
+       if (muic_if != NULL)
+               muic_if->is_dcp_charger = false;
+
+       muic_pdata->attached_dev = ATTACHED_DEV_NONE_MUIC;
+
+       return ret;
+}
+
+static int muic_core_attach_usb_util(struct muic_platform_data *muic_pdata,
+                       muic_attached_dev_t new_dev)
+{
+       int ret = 0;
+
+       ret = muic_core_switch_to_usb(muic_pdata, muic_pdata->usb_path);
+       if (ret)
+               return ret;
+
+       ret = muic_core_attach_charger(muic_pdata, new_dev);
+       return ret;
+}
+
+static int muic_core_attach_usb(struct muic_platform_data *muic_pdata,
+                       muic_attached_dev_t new_dev)
+{
+       int ret = 0;
+
+       if (muic_pdata->attached_dev == new_dev) {
+               pr_info("%s duplicated\n", __func__);
+               return ret;
+       }
+
+       pr_info("%s:%s\n", MUIC_CORE, __func__);
+
+       pr_info("%s\n", __func__);
+
+       ret = muic_core_attach_usb_util(muic_pdata, new_dev);
+       if (ret)
+               return ret;
+
+       return ret;
+}
+
+static int muic_core_attach_otg_usb(struct muic_platform_data *muic_pdata,
+                       muic_attached_dev_t new_dev)
+{
+       int ret = 0;
+       struct muic_interface_t *muic_if = (struct muic_interface_t *)muic_pdata->muic_if;
+
+       if (muic_pdata->attached_dev == new_dev) {
+               pr_info("%s duplicated\n", __func__);
+               return ret;
+       }
+
+       pr_info("%s\n", __func__);
+
+       if (muic_if->set_com_to_otg) {
+               ret = muic_if->set_com_to_otg(muic_pdata->drv_data);
+               if (ret) {
+                       pr_err("%s switch set err\n", __func__);
+                       return ret;
+               }
+       } else {
+               pr_err("%s func not set.\n", __func__);
+       }
+
+       muic_pdata->attached_dev = new_dev;
+
+       return ret;
+}
+
+static int muic_core_detach_otg_usb(struct muic_platform_data *muic_pdata)
+{
+       int ret = 0;
+       struct muic_interface_t *muic_if = (struct muic_interface_t *)muic_pdata->muic_if;
+
+       pr_info("%s : %d\n",
+               __func__, muic_pdata->attached_dev);
+
+       if (muic_if->set_com_to_open) {
+               ret = muic_if->set_com_to_open(muic_pdata->drv_data);
+               if (ret) {
+                       pr_err("%s switch set err\n", __func__);
+                       return ret;
+               }
+       } else {
+               pr_err("%s func not set.\n", __func__);
+       }
+
+       muic_pdata->attached_dev = ATTACHED_DEV_NONE_MUIC;
+
+       if (muic_pdata->usb_path == MUIC_PATH_USB_CP)
+               return ret;
+
+       return ret;
+}
+
+static int muic_core_detach_usb(struct muic_platform_data *muic_pdata)
+{
+       int ret = 0;
+       struct muic_interface_t *muic_if = (struct muic_interface_t *)muic_pdata->muic_if;
+
+       pr_info("%s : %d\n",
+               __func__, muic_pdata->attached_dev);
+
+       ret = muic_core_detach_charger(muic_pdata);
+       if (ret)
+               return ret;
+
+       if (muic_if->set_com_to_open) {
+               ret = muic_if->set_com_to_open(muic_pdata->drv_data);
+               if (ret) {
+                       pr_err("%s switch set err\n", __func__);
+                       return ret;
+               }
+       } else {
+               pr_err("%s func not set.\n", __func__);
+       }
+
+       muic_pdata->attached_dev = ATTACHED_DEV_NONE_MUIC;
+
+       if (muic_pdata->usb_path == MUIC_PATH_USB_CP)
+               return ret;
+
+       return ret;
+}
+
+static int muic_core_attach_deskdock(struct muic_platform_data *muic_pdata,
+                       muic_attached_dev_t new_dev, u8 vbvolt)
+{
+       int ret = 0;
+
+       pr_info("%s vbus(%x)\n", __func__, vbvolt);
+
+       ret = muic_core_switch_to_dock_audio(muic_pdata);
+       if (ret)
+               return ret;
+
+       if (vbvolt)
+               ret = muic_core_attach_charger(muic_pdata, new_dev);
+       else
+               ret = muic_core_detach_charger(muic_pdata);
+       if (ret)
+               return ret;
+
+       muic_pdata->attached_dev = new_dev;
+
+       return ret;
+}
+
+static int muic_core_detach_deskdock(struct muic_platform_data *muic_pdata)
+{
+       int ret = 0;
+
+       pr_info("%s\n", __func__);
+
+       ret = muic_core_switch_to_system_audio(muic_pdata);
+       if (ret)
+               pr_err("%s err changing audio path(%d)\n",
+                       __func__, ret);
+
+       ret = muic_core_detach_charger(muic_pdata);
+       if (ret)
+               pr_err("%s err detach_charger(%d)\n",
+                       __func__, ret);
+
+       muic_pdata->attached_dev = ATTACHED_DEV_NONE_MUIC;
+
+       return ret;
+}
+
+static int muic_core_attach_audiodock(struct muic_platform_data *muic_pdata,
+                       muic_attached_dev_t new_dev, u8 vbus)
+{
+       int ret = 0;
+       struct muic_interface_t *muic_if = (struct muic_interface_t *)muic_pdata->muic_if;
+
+       pr_info("%s\n", __func__);
+
+       if (!vbus) {
+               ret = muic_core_detach_charger(muic_pdata);
+               if (ret)
+                       pr_err("%s err detach_charger(%d)\n",
+                               __func__, ret);
+
+               if (muic_if->set_com_to_open) {
+                       ret = muic_if->set_com_to_open(muic_pdata->drv_data);
+                       if (ret)
+                               return ret;
+               }
+
+               muic_pdata->attached_dev = new_dev;
+
+               return ret;
+       }
+
+       ret = muic_core_attach_usb_util(muic_pdata, new_dev);
+       if (ret)
+               pr_err("%s attach_usb_util(%d)\n",
+                       __func__, ret);
+
+       muic_pdata->attached_dev = new_dev;
+
+       return ret;
+}
+
+static int muic_core_detach_audiodock(struct muic_platform_data *muic_pdata)
+{
+       int ret = 0;
+       struct muic_interface_t *muic_if = (struct muic_interface_t *)muic_pdata->muic_if;
+
+       pr_info("%s\n", __func__);
+
+       ret = muic_core_detach_charger(muic_pdata);
+       if (ret)
+               pr_err("%s err detach_charger(%d)\n",
+                       __func__, ret);
+
+       if (muic_if->set_com_to_open) {
+               ret = muic_if->set_com_to_open(muic_pdata->drv_data);
+               if (ret)
+                       return ret;
+       }
+
+       muic_pdata->attached_dev = ATTACHED_DEV_NONE_MUIC;
+
+       return ret;
+}
+
+static int muic_core_attach_jig_uart_boot_off(struct muic_platform_data *muic_pdata,
+                               muic_attached_dev_t new_dev)
+{
+       int ret = 0;
+
+       pr_info("%s(%d)\n",
+               __func__, new_dev);
+       ret = muic_core_switch_to_uart(muic_pdata, muic_pdata->uart_path);
+
+       ret = muic_core_attach_charger(muic_pdata, new_dev);
+
+       return ret;
+}
+
+static int muic_core_detach_jig_uart_boot_off(struct muic_platform_data *muic_pdata)
+{
+       int ret = 0;
+
+       pr_info("%s\n", __func__);
+
+       ret = muic_core_detach_charger(muic_pdata);
+       if (ret)
+               pr_err("%s err detach_charger(%d)\n", __func__, ret);
+
+       muic_pdata->attached_dev = ATTACHED_DEV_NONE_MUIC;
+
+       return ret;
+}
+
+static int muic_core_detach_jig_uart_boot_on(struct muic_platform_data *muic_pdata)
+{
+       pr_info("%s\n", __func__);
+
+       muic_pdata->attached_dev = ATTACHED_DEV_NONE_MUIC;
+
+       return 0;
+}
+
+static int muic_core_attach_jig_usb_boot_off(struct muic_platform_data *muic_pdata,
+       u8 vbvolt)
+{
+       int ret = 0;
+
+       if (muic_pdata->attached_dev == ATTACHED_DEV_JIG_USB_OFF_MUIC) {
+               pr_info("%s duplicated\n", __func__);
+               return ret;
+       }
+
+       pr_info("%s\n", __func__);
+
+       ret = muic_core_attach_usb_util(muic_pdata, ATTACHED_DEV_JIG_USB_OFF_MUIC);
+       if (ret)
+               return ret;
+
+       return ret;
+}
+
+static int muic_core_attach_jig_usb_boot_on(struct muic_platform_data *muic_pdata,
+       u8 vbvolt)
+{
+       int ret = 0;
+
+       if (muic_pdata->attached_dev == ATTACHED_DEV_JIG_USB_ON_MUIC) {
+               pr_info("%s duplicated\n", __func__);
+               return ret;
+       }
+
+       pr_info("%s\n", __func__);
+
+       ret = muic_core_attach_usb_util(muic_pdata, ATTACHED_DEV_JIG_USB_ON_MUIC);
+       if (ret)
+               return ret;
+
+       return ret;
+}
+
+static int muic_core_handle_attached_prev_dev(struct muic_platform_data *muic_pdata,
+       muic_attached_dev_t new_dev, bool *noti)
+{
+       int ret = 0;
+
+       pr_info("%s attached_dev: %d, new_dev: %d\n",
+               __func__, muic_pdata->attached_dev, new_dev);
+
+       switch (muic_pdata->attached_dev) {
+       case ATTACHED_DEV_USB_MUIC:
+       case ATTACHED_DEV_CDP_MUIC:
+       case ATTACHED_DEV_JIG_USB_OFF_MUIC:
+       case ATTACHED_DEV_JIG_USB_ON_MUIC:
+       case ATTACHED_DEV_TIMEOUT_OPEN_MUIC:
+               if (new_dev != muic_pdata->attached_dev) {
+                       pr_info("%s new(%d)!=attached(%d)\n",
+                               __func__, new_dev, muic_pdata->attached_dev);
+                       ret = muic_core_detach_usb(muic_pdata);
+               }
+               break;
+       case ATTACHED_DEV_OTG_MUIC:
+       /* OTG -> LANHUB, meaning TA is attached to LANHUB(OTG) */
+               if (new_dev != muic_pdata->attached_dev) {
+                       pr_info("%s new(%d)!=attached(%d)",
+                               __func__, new_dev, muic_pdata->attached_dev);
+                       ret = muic_core_detach_otg_usb(muic_pdata);
+               }
+               break;
+
+       case ATTACHED_DEV_AUDIODOCK_MUIC:
+               if (new_dev != muic_pdata->attached_dev) {
+                       pr_info("%s new(%d)!=attached(%d)\n",
+                               __func__, new_dev, muic_pdata->attached_dev);
+                       ret = muic_core_detach_audiodock(muic_pdata);
+               }
+               break;
+
+       case ATTACHED_DEV_TA_MUIC:
+       case ATTACHED_DEV_UNDEFINED_CHARGING_MUIC:
+               if (new_dev != muic_pdata->attached_dev) {
+                       pr_info("%s new(%d)!=attached(%d)\n",
+                               __func__, new_dev, muic_pdata->attached_dev);
+                       ret = muic_core_detach_charger(muic_pdata);
+               }
+               break;
+
+       case ATTACHED_DEV_JIG_UART_OFF_VB_OTG_MUIC:
+       case ATTACHED_DEV_JIG_UART_OFF_VB_FG_MUIC:
+       case ATTACHED_DEV_JIG_UART_OFF_VB_MUIC:
+       case ATTACHED_DEV_JIG_UART_OFF_MUIC:
+               if (new_dev != ATTACHED_DEV_JIG_UART_OFF_MUIC) {
+                       pr_info("%s new(%d)!=attached(%d)\n",
+                               __func__, new_dev, muic_pdata->attached_dev);
+                       ret = muic_core_detach_jig_uart_boot_off(muic_pdata);
+               }
+               break;
+
+       case ATTACHED_DEV_JIG_UART_ON_MUIC:
+       case ATTACHED_DEV_JIG_UART_ON_VB_MUIC:
+               if (new_dev != muic_pdata->attached_dev) {
+                       pr_info("%s new(%d)!=attached(%d)\n",
+                               __func__, new_dev, muic_pdata->attached_dev);
+                       ret = muic_core_detach_jig_uart_boot_off(muic_pdata);
+               }
+               break;
+       case ATTACHED_DEV_DESKDOCK_MUIC:
+               if (new_dev != muic_pdata->attached_dev) {
+                       pr_info("%s new(%d)!=attached(%d)\n",
+                               __func__, new_dev, muic_pdata->attached_dev);
+
+                       if (muic_pdata->is_factory_start)
+                               ret = muic_core_detach_deskdock(muic_pdata);
+                       else {
+                               *noti = false;
+                               ret = muic_core_detach_jig_uart_boot_on(muic_pdata);
+                       }
+               }
+               break;
+       case ATTACHED_DEV_NONE_MUIC:
+       default:
+               break;
+       }
+
+       if (*noti) {
+               if (!muic_pdata->suspended) {
+                       MUIC_SEND_NOTI_DETACH_ALL(muic_pdata->attached_dev);
+               } else
+                       muic_pdata->need_to_noti = true;
+       }
+
+       return ret;
+}
+
+static int muic_core_handle_attached_new_dev(struct muic_platform_data *muic_pdata,
+       muic_attached_dev_t new_dev, bool *noti)
+{
+       int ret = 0;
+       struct muic_interface_t *muic_if = (struct muic_interface_t *)muic_pdata->muic_if;
+
+       pr_info("%s : muic_pdata->attached_dev: %d, new_dev: %d\n",
+               __func__, muic_pdata->attached_dev, new_dev);
+
+       switch (new_dev) {
+       case ATTACHED_DEV_USB_MUIC:
+       case ATTACHED_DEV_CDP_MUIC:
+       case ATTACHED_DEV_TIMEOUT_OPEN_MUIC:
+               ret = muic_core_attach_usb(muic_pdata, new_dev);
+               break;
+       case ATTACHED_DEV_OTG_MUIC:
+               ret = muic_core_attach_otg_usb(muic_pdata, new_dev);
+               break;
+       case ATTACHED_DEV_AUDIODOCK_MUIC:
+               ret = muic_core_attach_audiodock(muic_pdata, new_dev, muic_pdata->vbvolt);
+               break;
+       case ATTACHED_DEV_TA_MUIC:
+       case ATTACHED_DEV_UNDEFINED_CHARGING_MUIC:
+       case ATTACHED_DEV_UNSUPPORTED_ID_VB_MUIC:
+               MUIC_PDATA_FUNC(muic_if->set_com_to_open, muic_pdata->drv_data, &ret);
+               ret = muic_core_attach_charger(muic_pdata, new_dev);
+#if defined(CONFIG_MUIC_S2MU004_HV)
+               MUIC_PDATA_FUNC(muic_if->check_afc_ready, muic_pdata->drv_data, &ret);
+#endif /* CONFIG_MUIC_S2MU004_HV */
+               break;
+       case ATTACHED_DEV_JIG_UART_OFF_VB_OTG_MUIC:
+       case ATTACHED_DEV_JIG_UART_OFF_VB_FG_MUIC:
+       case ATTACHED_DEV_JIG_UART_OFF_VB_MUIC:
+       case ATTACHED_DEV_JIG_UART_OFF_MUIC:
+       case ATTACHED_DEV_JIG_UART_ON_MUIC:
+       case ATTACHED_DEV_JIG_UART_ON_VB_MUIC:
+               muic_pdata->is_jig_on = true;
+               ret = muic_core_attach_jig_uart_boot_off(muic_pdata, new_dev);
+               break;
+       case ATTACHED_DEV_JIG_USB_OFF_MUIC:
+               muic_pdata->is_jig_on = true;
+               ret = muic_core_attach_jig_usb_boot_off(muic_pdata, muic_pdata->vbvolt);
+               break;
+       case ATTACHED_DEV_JIG_USB_ON_MUIC:
+               muic_pdata->is_jig_on = true;
+               ret = muic_core_attach_jig_usb_boot_on(muic_pdata, muic_pdata->vbvolt);
+               break;
+       case ATTACHED_DEV_DESKDOCK_MUIC:
+               ret = muic_core_attach_deskdock(muic_pdata, new_dev, muic_pdata->vbvolt);
+               break;
+       case ATTACHED_DEV_UNKNOWN_MUIC:
+               MUIC_PDATA_FUNC(muic_if->set_com_to_open, muic_pdata->drv_data, &ret);
+               ret = muic_core_attach_charger(muic_pdata, new_dev);
+               break;
+       case ATTACHED_DEV_TYPE3_MUIC:
+               muic_pdata->attached_dev = new_dev;
+               break;
+       case ATTACHED_DEV_UNDEFINED_RANGE_MUIC:
+               muic_pdata->attached_dev = new_dev;
+               break;
+       default:
+               *noti = false;
+               pr_info("%s unsupported dev=%d, adc=0x%x, vbus=%c\n",
+                       __func__, new_dev, muic_pdata->adc,
+                       (muic_pdata->vbvolt ? 'O' : 'X'));
+               break;
+       }
+
+/* TODO: There is no needs to use JIGB pin by MUIC if CCIC is supported */
+#ifndef CONFIG_USE_CCIC
+       MUIC_PDATA_FUNC(muic_if->set_jig_ctrl_on, muic_pdata->drv_data, &ret);
+#endif
+
+       if (ret)
+               pr_err("%s something wrong %d (ERR=%d)\n",
+                       __func__, new_dev, ret);
+
+       pr_info("%s:%s done\n", MUIC_CORE, __func__);
+
+#if defined(CONFIG_MUIC_S2MU004_HV)
+       MUIC_PDATA_FUNC_MULTI_PARAM(muic_if->check_id_err, muic_pdata->drv_data,
+                       new_dev, &ret);
+       new_dev = ret;
+#endif /* CONFIG_MUIC_S2MU004_HV */
+
+       return ret;
+}
+
+bool muic_core_is_cable_attached(struct muic_platform_data *muic_pdata)
+{
+       pr_info("%s attached_dev=%d\n", __func__, muic_pdata->attached_dev);
+
+       switch (muic_pdata->attached_dev) {
+       case ATTACHED_DEV_NONE_MUIC:
+       case ATTACHED_DEV_UNKNOWN_MUIC:
+               return false;
+       default:
+               return true;
+       }
+}
+
+bool muic_core_get_ccic_cable_state(struct muic_platform_data *muic_pdata)
+{
+       pr_info("%s attached_dev=%d\n", __func__, muic_pdata->attached_dev);
+
+       switch (muic_pdata->attached_dev) {
+       case ATTACHED_DEV_JIG_USB_OFF_MUIC:
+       case ATTACHED_DEV_JIG_USB_ON_MUIC:
+       case ATTACHED_DEV_JIG_UART_OFF_MUIC:
+       case ATTACHED_DEV_JIG_UART_OFF_VB_MUIC:
+       case ATTACHED_DEV_JIG_UART_ON_MUIC:
+       case ATTACHED_DEV_JIG_UART_ON_VB_MUIC:
+       case ATTACHED_DEV_OTG_MUIC:
+       case ATTACHED_DEV_TYPE3_CHARGER_MUIC:
+               return true;
+       default:
+               return false;
+       }
+}
+
+int muic_core_handle_attach(struct muic_platform_data *muic_pdata,
+                       muic_attached_dev_t new_dev, int adc, u8 vbvolt)
+{
+       int ret = 0;
+       bool noti = (new_dev != muic_pdata->attached_dev) ? true : false;
+
+       muic_pdata->is_jig_on = false;
+       pr_info("%s attached_dev: %d, new_dev: %d\n",
+               __func__, muic_pdata->attached_dev, new_dev);
+       muic_pdata->adc = adc;
+       muic_pdata->vbvolt = vbvolt;
+
+       ret = muic_core_handle_attached_prev_dev(muic_pdata, new_dev, &noti);
+       if (ret)
+               pr_err("%s prev_dev failed\n", __func__);
+
+       ret = muic_core_handle_attached_new_dev(muic_pdata, new_dev, &noti);
+       if (ret)
+               pr_err("%s new_dev failed\n", __func__);
+
+       if (noti) {
+               if (!muic_pdata->suspended) {
+                       MUIC_SEND_NOTI_ATTACH_ALL(new_dev);
+               } else
+                       muic_pdata->need_to_noti = true;
+       }
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(muic_core_handle_attach);
+
+int muic_core_handle_detach(struct muic_platform_data *muic_pdata)
+{
+       int ret = 0;
+       bool noti = true;
+       struct muic_interface_t *muic_if = (struct muic_interface_t *)muic_pdata->muic_if;
+       muic_attached_dev_t prev_dev = muic_pdata->attached_dev;
+
+       pr_info("%s attached_dev: %d\n",
+               __func__, muic_pdata->attached_dev);
+
+       MUIC_PDATA_FUNC(muic_if->set_com_to_open, muic_pdata->drv_data, &ret);
+
+       switch (muic_pdata->attached_dev) {
+       case ATTACHED_DEV_JIG_USB_OFF_MUIC:
+       case ATTACHED_DEV_JIG_USB_ON_MUIC:
+       case ATTACHED_DEV_USB_MUIC:
+       case ATTACHED_DEV_CDP_MUIC:
+       case ATTACHED_DEV_TIMEOUT_OPEN_MUIC:
+#if defined(CONFIG_SEC_FACTORY)
+       case ATTACHED_DEV_CARKIT_MUIC:
+#endif
+               ret = muic_core_detach_usb(muic_pdata);
+               break;
+       case ATTACHED_DEV_OTG_MUIC:
+               ret = muic_core_detach_otg_usb(muic_pdata);
+               break;
+       case ATTACHED_DEV_TA_MUIC:
+       case ATTACHED_DEV_UNDEFINED_CHARGING_MUIC:
+               ret = muic_core_detach_charger(muic_pdata);
+#if defined(CONFIG_MUIC_S2MU004_HV)
+               MUIC_PDATA_FUNC(muic_if->reset_hvcontrol_reg, muic_pdata->drv_data, &ret);
+#endif
+               break;
+       case ATTACHED_DEV_JIG_UART_OFF_VB_OTG_MUIC:
+       case ATTACHED_DEV_JIG_UART_OFF_VB_FG_MUIC:
+       case ATTACHED_DEV_JIG_UART_OFF_VB_MUIC:
+       case ATTACHED_DEV_JIG_UART_OFF_MUIC:
+       case ATTACHED_DEV_JIG_UART_ON_MUIC:
+       case ATTACHED_DEV_JIG_UART_ON_VB_MUIC:
+               ret = muic_core_detach_jig_uart_boot_off(muic_pdata);
+               break;
+       case ATTACHED_DEV_DESKDOCK_MUIC:
+               if (muic_pdata->is_factory_start)
+                       ret = muic_core_detach_deskdock(muic_pdata);
+               else {
+                       noti = false;
+                       ret = muic_core_detach_jig_uart_boot_on(muic_pdata);
+               }
+               break;
+       case ATTACHED_DEV_AUDIODOCK_MUIC:
+               ret = muic_core_detach_audiodock(muic_pdata);
+               break;
+       case ATTACHED_DEV_NONE_MUIC:
+               pr_info("%s duplicated(NONE)\n", __func__);
+               break;
+       case ATTACHED_DEV_UNKNOWN_MUIC:
+               pr_info("%s UNKNOWN\n", __func__);
+               ret = muic_core_detach_charger(muic_pdata);
+               muic_pdata->attached_dev = ATTACHED_DEV_NONE_MUIC;
+               break;
+#if defined(CONFIG_MUIC_S2MU004_HV)
+       case ATTACHED_DEV_AFC_CHARGER_PREPARE_MUIC:
+       case ATTACHED_DEV_AFC_CHARGER_PREPARE_DUPLI_MUIC:
+       case ATTACHED_DEV_AFC_CHARGER_5V_MUIC:
+       case ATTACHED_DEV_AFC_CHARGER_5V_DUPLI_MUIC:
+       case ATTACHED_DEV_AFC_CHARGER_9V_MUIC:
+       case ATTACHED_DEV_QC_CHARGER_5V_MUIC:
+       case ATTACHED_DEV_QC_CHARGER_9V_MUIC:
+       case ATTACHED_DEV_QC_CHARGER_PREPARE_MUIC:
+       case ATTACHED_DEV_AFC_CHARGER_ERR_V_MUIC:
+       case ATTACHED_DEV_AFC_CHARGER_ERR_V_DUPLI_MUIC:
+               ret = muic_core_detach_charger(muic_pdata);
+               MUIC_PDATA_FUNC(muic_if->reset_hvcontrol_reg,
+                       muic_pdata->drv_data, &ret);
+               break;
+#endif
+       default:
+               noti = false;
+               pr_info("%s invalid type(%d)\n",
+                       __func__, muic_pdata->attached_dev);
+               muic_pdata->attached_dev = ATTACHED_DEV_NONE_MUIC;
+               break;
+       }
+       if (ret)
+               pr_err("%s something wrong %d (ERR=%d)\n",
+                       __func__, muic_pdata->attached_dev, ret);
+
+       if (noti) {
+               if (!muic_pdata->suspended) {
+                       MUIC_SEND_NOTI_DETACH_ALL(prev_dev);
+               } else
+                       muic_pdata->need_to_noti = true;
+       }
+       return ret;
+}
+EXPORT_SYMBOL_GPL(muic_core_handle_detach);
+
+struct muic_platform_data *muic_core_init(void *drv_data)
+{
+       struct muic_platform_data *muic_pdata;
+
+       muic_pdata = kzalloc(sizeof(*muic_pdata), GFP_KERNEL);
+       if (unlikely(!muic_pdata)) {
+               pr_err("%s: failed to allocate driver data\n", __func__);
+               return NULL;
+       }
+
+       muic_pdata->drv_data = drv_data;
+       muic_pdata->attached_dev = ATTACHED_DEV_NONE_MUIC;
+       muic_pdata->cleanup_switch_dev_cb       = muic_cleanup_switch_dev_cb;
+       muic_pdata->is_usb_ready = false;
+       muic_pdata->is_factory_start = false;
+       muic_pdata->is_rustproof = muic_pdata->rustproof_on;
+       muic_pdata->init_gpio_cb = muic_init_gpio_cb;
+       muic_init_switch_dev_cb();
+
+       return muic_pdata;
+}
+
+void muic_core_exit(struct muic_platform_data *muic_pdata)
+{
+       kfree(muic_pdata);
+}
diff --git a/drivers/muic/muic_manager.c b/drivers/muic/muic_manager.c
new file mode 100644 (file)
index 0000000..2043836
--- /dev/null
@@ -0,0 +1,873 @@
+/*
+ * muic_ccic.c
+ *
+ * Copyright (C) 2014 Samsung Electronics
+ * Thomas Ryu <smilesr.ryu@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
+ *
+ */
+#define pr_fmt(fmt)    "[MUIC] " fmt
+
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#if defined(CONFIG_USB_HOST_NOTIFY)
+#include <linux/host_notify.h>
+#endif
+#include <linux/string.h>
+#if defined(CONFIG_OF)
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#endif
+
+#include <linux/muic/muic_core.h>
+#if defined(CONFIG_IFCONN_NOTIFIER)
+#include <linux/ifconn/ifconn_notifier.h>
+#endif
+#if defined(CONFIG_MUIC_NOTIFIER)
+#include <linux/muic/s2mu004-muic-notifier.h>
+#endif
+#include <linux/muic/muic_interface.h>
+
+#if defined(CONFIG_CCIC_NOTIFIER)
+#include <linux/ccic/ccic_notifier.h>
+#endif
+
+#if defined(CONFIG_USB_TYPEC_MANAGER_NOTIFIER)
+#include <linux/usb/manager/usb_typec_manager_notifier.h>
+#endif
+
+#if defined(CONFIG_MUIC_HV)
+#include "muic_hv.h"
+#endif
+
+#define MUIC_CCIC_NOTI_ATTACH (1)
+#define MUIC_CCIC_NOTI_DETACH (-1)
+#define MUIC_CCIC_NOTI_UNDEFINED (0)
+
+static int __ccic_info;
+static struct ccic_rid_desc_t ccic_rid_tbl[] = {
+       [CCIC_RID_UNDEFINED] = {"UNDEFINED", ATTACHED_DEV_NONE_MUIC},
+       [CCIC_RID_000K] = {"000K", ATTACHED_DEV_OTG_MUIC},
+       [CCIC_RID_001K] = {"001K", ATTACHED_DEV_MHL_MUIC},
+       [CCIC_RID_255K] = {"255K", ATTACHED_DEV_JIG_USB_OFF_MUIC},
+       [CCIC_RID_301K] = {"301K", ATTACHED_DEV_JIG_USB_ON_MUIC},
+       [CCIC_RID_523K] = {"523K", ATTACHED_DEV_JIG_UART_OFF_MUIC},
+       [CCIC_RID_619K] = {"619K", ATTACHED_DEV_JIG_UART_ON_MUIC},
+       [CCIC_RID_OPEN] = {"OPEN", ATTACHED_DEV_NONE_MUIC},
+};
+
+/*
+ * __ccic_info :
+ * b'0: 1 if an active ccic is present,
+ *        0 when muic works without ccic chip or
+ *              no ccic Noti. registration is needed
+ *              even though a ccic chip is present.
+ */
+static int set_ccic_info(char *str)
+{
+       get_option(&str, &__ccic_info);
+
+       pr_info("%s: ccic_info: 0x%04x\n", __func__, __ccic_info);
+
+       return __ccic_info;
+}
+
+__setup("ccic_info=", set_ccic_info);
+
+int get_ccic_info(void)
+{
+       return __ccic_info;
+}
+
+static void _muic_manager_switch_uart_path(struct muic_interface_t *muic_if, int path)
+{
+       if (muic_if->pdata->gpio_uart_sel)
+               muic_if->set_gpio_uart_sel(muic_if->muic_data, path);
+
+       if (muic_if->set_switch_to_uart)
+               muic_if->set_switch_to_uart(muic_if->muic_data);
+       else
+               pr_err("%s:function not set!\n", __func__);
+}
+
+static void _muic_manager_switch_usb_path(struct muic_interface_t *muic_if, int path)
+{
+       if (muic_if->pdata->gpio_usb_sel)
+               muic_if->set_gpio_usb_sel(muic_if->muic_data, path);
+
+       if (muic_if->set_switch_to_usb)
+               muic_if->set_switch_to_usb(muic_if->muic_data);
+       else
+               pr_err("%s:function not set!\n", __func__);
+}
+
+static int muic_manager_switch_path(struct muic_interface_t *muic_if, int path)
+{
+#if defined(CONFIG_MUIC_HV)
+       hv_clear_hvcontrol(muic_if->phv);
+#endif
+       switch (path) {
+       case MUIC_PATH_OPEN:
+               muic_if->set_com_to_open(muic_if->muic_data);
+               break;
+
+       case MUIC_PATH_USB_AP:
+       case MUIC_PATH_USB_CP:
+               _muic_manager_switch_usb_path(muic_if, muic_if->pdata->usb_path);
+               break;
+       case MUIC_PATH_UART_AP:
+       case MUIC_PATH_UART_CP:
+               _muic_manager_switch_uart_path(muic_if, muic_if->pdata->uart_path);
+               break;
+
+       default:
+               pr_err("%s:A wrong com path!\n", __func__);
+               return -1;
+       }
+
+       return 0;
+}
+
+static int muic_manager_get_vbus(struct muic_interface_t *muic_if)
+{
+       int ret = 0;
+
+       ret = muic_if->get_vbus(muic_if->muic_data);
+
+       return ret;
+}
+
+static bool muic_manager_is_supported_dev(int attached_dev)
+{
+       switch (attached_dev) {
+       case ATTACHED_DEV_USB_MUIC:
+       case ATTACHED_DEV_CDP_MUIC:
+       case ATTACHED_DEV_TA_MUIC:
+       case ATTACHED_DEV_JIG_UART_OFF_MUIC:
+       case ATTACHED_DEV_JIG_UART_OFF_VB_MUIC:
+       case ATTACHED_DEV_JIG_UART_ON_MUIC:
+       case ATTACHED_DEV_JIG_UART_ON_VB_MUIC:
+       case ATTACHED_DEV_JIG_USB_OFF_MUIC:
+       case ATTACHED_DEV_JIG_USB_ON_MUIC:
+       case ATTACHED_DEV_OTG_MUIC:
+       case ATTACHED_DEV_AFC_CHARGER_5V_MUIC:
+       case ATTACHED_DEV_AFC_CHARGER_9V_MUIC:
+       case ATTACHED_DEV_QC_CHARGER_5V_MUIC:
+       case ATTACHED_DEV_QC_CHARGER_9V_MUIC:
+               return true;
+       default:
+               break;
+       }
+
+       return false;
+}
+
+int muic_manager_is_ccic_supported_dev(muic_attached_dev_t new_dev)
+{
+       switch (new_dev) {
+               /* Legacy TA/USB. Noti. will be sent when ATTACH is received from CCIC. */
+       case ATTACHED_DEV_USB_MUIC:
+       case ATTACHED_DEV_CDP_MUIC:
+       case ATTACHED_DEV_TA_MUIC:
+       case ATTACHED_DEV_TIMEOUT_OPEN_MUIC:
+               return 1;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+void muic_manager_handle_ccic_detach(struct muic_interface_t *muic_if)
+{
+       struct ccic_desc_t *ccic = muic_if->ccic;
+       struct muic_platform_data *pdata = muic_if->pdata;
+
+       pr_info("%s\n", __func__);
+
+#if defined(CONFIG_MUIC_HV)
+       hv_do_detach(muic_if->phv);
+#endif
+       if (ccic->ccic_evt_rprd) {
+               /* FIXME : pvendor
+                * if (pvendor && pvendor->enable_chgdet)
+                * pvendor->enable_chgdet(muic_if->regmaccic, 1);
+                */
+       }
+
+       muic_manager_switch_path(muic_if, MUIC_PATH_OPEN);
+       if (muic_manager_is_supported_dev(ccic->attached_dev)) {
+               MUIC_SEND_NOTI_DETACH(ccic->attached_dev);
+       } else if (muic_if->legacy_dev != ATTACHED_DEV_NONE_MUIC) {
+               MUIC_SEND_NOTI_DETACH(muic_if->legacy_dev);
+       }
+
+       if (muic_core_get_ccic_cable_state(pdata))
+               muic_if->set_cable_state(muic_if->muic_data, ATTACHED_DEV_NONE_MUIC);
+
+       if (pdata->jig_uart_cb)
+               pdata->jig_uart_cb(0);
+
+       /* Reset status & flags */
+       ccic->attached_dev = 0;
+       ccic->ccic_evt_rid = 0;
+       ccic->ccic_evt_rprd = 0;
+       ccic->ccic_evt_roleswap = 0;
+       ccic->ccic_evt_dcdcnt = 0;
+       ccic->ccic_evt_attached = MUIC_CCIC_NOTI_UNDEFINED;
+
+       muic_if->legacy_dev = 0;
+       muic_if->attached_dev = 0;
+#if defined(CONFIG_MUIC_HV)
+       muic_if->phv->attached_dev = 0;
+#endif
+       muic_if->is_dcdtmr_intr = false;
+}
+
+void muic_manager_set_legacy_dev(struct muic_interface_t *muic_if, int new_dev)
+{
+       pr_info("%s: %d->%d\n", __func__, muic_if->legacy_dev, new_dev);
+
+       muic_if->legacy_dev = new_dev;
+}
+
+/* Get the charger type from muic interrupt or by reading the register directly */
+int muic_manager_get_legacy_dev(struct muic_interface_t *muic_if)
+{
+       return muic_if->legacy_dev;
+}
+
+static void muic_manager_show_status(struct muic_interface_t *muic_if)
+{
+       struct ccic_desc_t *ccic = muic_if->ccic;
+
+       pr_info("%s: attached_dev:%d rid:%d rprd:%d attached:%d legacy_dev:%d\n", __func__,
+                       ccic->attached_dev, ccic->ccic_evt_rid, ccic->ccic_evt_rprd,
+                       ccic->ccic_evt_attached, muic_if->legacy_dev);
+}
+
+int muic_manager_dcd_rescan(struct muic_interface_t *muic_if)
+{
+       struct ccic_desc_t *ccic = muic_if->ccic;
+       int vbus = muic_manager_get_vbus(muic_if);
+
+       pr_info("%s : ccic_evt_attached(%d), is_dcdtmr_intr(%d), ccic_evt_dcdcnt(%d)\n",
+                       __func__, ccic->ccic_evt_attached, muic_if->is_dcdtmr_intr, ccic->ccic_evt_dcdcnt);
+
+       if (!(muic_if->opmode & OPMODE_DEVICE)) {
+               pr_info("%s : it's SMD board, skip rescan", __func__);
+               goto SKIP_RESCAN;
+       }
+
+       /* W/A for Incomplete insertion case */
+       if (muic_if->is_dcdtmr_intr && vbus && ccic->ccic_evt_dcdcnt < 1) {
+               pr_info("%s: Incomplete insertion. Do chgdet again\n", __func__);
+               ccic->ccic_evt_dcdcnt++;
+
+               if (muic_if->set_dcd_rescan != NULL)
+                       muic_if->set_dcd_rescan(muic_if->muic_data);
+
+               return 0;
+       }
+
+SKIP_RESCAN:
+       return 1;
+}
+
+static int muic_manager_handle_legacy_dev(struct muic_interface_t *muic_if)
+{
+       struct ccic_desc_t *ccic = muic_if->ccic;
+       int attached_dev = 0;
+
+       pr_info("%s: vbvolt:%d legacy_dev:%d\n", __func__,
+               muic_if->vps.t.vbvolt, muic_if->legacy_dev);
+
+       /* 1. Run a charger detection algorithm manually if necessary. */
+       msleep(200);
+
+       /* 2. Get the result by polling or via an interrupt */
+       attached_dev = muic_manager_get_legacy_dev(muic_if);
+       pr_info("%s: detected legacy_dev=%d\n", __func__, attached_dev);
+
+       /* 3. Noti. if supported. */
+       if (!muic_manager_is_ccic_supported_dev(attached_dev)) {
+               pr_info("%s: Unsupported legacy_dev=%d\n", __func__, attached_dev);
+               return 0;
+       }
+
+       if (muic_manager_is_supported_dev(ccic->attached_dev)) {
+               MUIC_SEND_NOTI_DETACH(ccic->attached_dev);
+               ccic->attached_dev = 0;
+       } else if (muic_if->legacy_dev != ATTACHED_DEV_NONE_MUIC) {
+               MUIC_SEND_NOTI_DETACH(muic_if->legacy_dev);
+               muic_if->legacy_dev = 0;
+       }
+
+       ccic->attached_dev = attached_dev;
+       MUIC_SEND_NOTI_ATTACH(attached_dev);
+
+       return 0;
+}
+
+void muic_manager_init_dev_desc(struct muic_interface_t *muic_if)
+{
+       struct ccic_desc_t *ccic = muic_if->ccic;
+
+       pr_info("%s\n", __func__);
+       ccic->attached_dev = 0;
+       ccic->ccic_evt_rid = 0;
+       ccic->ccic_evt_rprd = 0;
+       ccic->ccic_evt_roleswap = 0;
+       ccic->ccic_evt_dcdcnt = 0;
+       ccic->ccic_evt_attached = MUIC_CCIC_NOTI_UNDEFINED;
+}
+
+static int muic_manager_conv_rid_to_dev(struct muic_interface_t *muic_if, int rid, int vbus)
+{
+       int attached_dev = 0;
+
+       pr_info("%s rid=%d vbus=%d\n", __func__, rid, vbus);
+
+       if (rid < 0 || rid > CCIC_RID_OPEN) {
+               pr_err("%s:Out of RID range: %d\n", __func__, rid);
+               return 0;
+       }
+
+       if ((rid == CCIC_RID_619K) && vbus)
+               attached_dev = ATTACHED_DEV_JIG_UART_ON_VB_MUIC;
+       else
+               attached_dev = muic_if->ccic->rid_desc[rid].attached_dev;
+
+       return attached_dev;
+}
+
+static bool muic_manager_is_valid_rid_open(struct muic_interface_t *muic_if, int vbus)
+{
+       int i, retry = 5;
+
+       if (vbus)
+               return true;
+
+       for (i = 0; i < retry; i++) {
+               pr_info("%s: %dth ...\n", __func__, i);
+               msleep(20);
+               if (muic_manager_get_vbus(muic_if))
+                       return 1;
+       }
+
+       return 0;
+}
+
+#ifdef CONFIG_IFCONN_NOTIFIER
+static int muic_manager_handle_ccic_attach(struct muic_interface_t *muic_if, void *data)
+{
+#ifdef CONFIG_IFCONN_NOTIFIER
+       struct ifconn_notifier_template *pnoti =
+           (struct ifconn_notifier_template *)data;
+#else
+       CC_NOTI_ATTACH_TYPEDEF *pnoti = (CC_NOTI_ATTACH_TYPEDEF *)data;
+#endif
+
+       pr_info("%s: src:%d dest:%d id:%d attach:%d cable_type:%d rprd:%d\n",
+               __func__, pnoti->src, pnoti->dest, pnoti->id, pnoti->attach,
+               pnoti->cable_type, pnoti->rprd);
+
+       /* Attached */
+       if (pnoti->event == IFCONN_NOTIFY_EVENT_ATTACH) {
+               pr_info("%s: Attach\n", __func__);
+       } else {
+               muic_manager_handle_ccic_detach(muic_if);
+       }
+
+       return 0;
+}
+#else
+static int muic_manager_handle_ccic_attach(struct muic_interface_t *muic_if, void *data)
+{
+       struct ccic_desc_t *ccic = muic_if->ccic;
+#ifdef CONFIG_IFCONN_NOTIFIER
+       struct ifconn_notifier_template *pnoti =
+           (struct ifconn_notifier_template *)data;
+#else
+       CC_NOTI_ATTACH_TYPEDEF *pnoti = (CC_NOTI_ATTACH_TYPEDEF *) data;
+#endif
+       int vbus = muic_manager_get_vbus(muic_if);
+
+       pr_info("%s: src:%d dest:%d id:%d attach:%d cable_type:%d rprd:%d\n",
+               __func__, pnoti->src, pnoti->dest, pnoti->id, pnoti->attach,
+               pnoti->cable_type, pnoti->rprd);
+
+       ccic->ccic_evt_attached = pnoti->attach ?
+           MUIC_CCIC_NOTI_ATTACH : MUIC_CCIC_NOTI_DETACH;
+
+       /* Attached */
+       if (ccic->ccic_evt_attached == MUIC_CCIC_NOTI_ATTACH) {
+               pr_info("%s: Attach\n", __func__);
+
+               if (ccic->ccic_evt_roleswap) {
+                       pr_info("%s: roleswap event, attach USB\n", __func__);
+                       ccic->ccic_evt_roleswap = 0;
+                       if (muic_manager_get_vbus(muic_if)) {
+                               ccic->attached_dev = ATTACHED_DEV_USB_MUIC;
+                               MUIC_SEND_NOTI_ATTACH(ccic->attached_dev);
+                       }
+                       return 0;
+               }
+
+               if (pnoti->rprd) {
+                       pr_info("%s: RPRD\n", __func__);
+                       ccic->ccic_evt_rprd = 1;
+                       ccic->attached_dev = ATTACHED_DEV_OTG_MUIC;
+                       muic_if->set_cable_state(muic_if->muic_data, ccic->attached_dev);
+                       muic_manager_switch_path(muic_if, MUIC_PATH_USB_AP);
+                       return 0;
+               }
+
+               if (muic_if->is_afc_reset) {
+                       pr_info("%s: DCD RESCAN after afc reset\n", __func__);
+                       muic_if->is_afc_reset = false;
+                       if (muic_if->set_dcd_rescan != NULL && !muic_if->is_dcp_charger)
+                               muic_if->set_dcd_rescan(muic_if->muic_data);
+               }
+
+               if (muic_manager_is_valid_rid_open(muic_if, vbus))
+                       pr_info("%s: Valid VBUS-> handled in irq handler\n", __func__);
+               else
+                       pr_info("%s: No VBUS-> doing nothing.\n", __func__);
+
+               /* CCIC ATTACH means NO WATER */
+               if (muic_if->afc_water_disable) {
+                       pr_info("%s: Water is not detected, AFC Enable\n", __func__);
+                       muic_if->afc_water_disable = false;
+               }
+
+               /* W/A for Incomplete insertion case */
+               ccic->ccic_evt_dcdcnt = 0;
+               if (muic_if->is_dcdtmr_intr && vbus) {
+                       if (muic_if->vps.t.chgdetrun) {
+                               pr_info("%s: Incomplete insertion. Chgdet runnung\n", __func__);
+                               return 0;
+                       }
+                       pr_info("%s: Incomplete insertion. Do chgdet again\n", __func__);
+                       muic_if->is_dcdtmr_intr = false;
+
+                       if (muic_if->set_dcd_rescan != NULL)
+                               muic_if->set_dcd_rescan(muic_if->muic_data);
+               }
+
+       } else {
+               if (pnoti->rprd) {
+                       /* Role swap detach: attached=0, rprd=1 */
+                       pr_info("%s: role swap event\n", __func__);
+                       ccic->ccic_evt_roleswap = 1;
+               } else {
+                       /* Detached */
+                       muic_manager_handle_ccic_detach(muic_if);
+               }
+       }
+
+       return 0;
+}
+
+#endif
+static int muic_manager_handle_ccic_factory_jig(struct muic_interface_t *muic_if, int rid, int vbus)
+{
+       struct ccic_desc_t *ccic = muic_if->ccic;
+       struct muic_platform_data *pdata = muic_if->pdata;
+       int attached_dev = 0;
+
+       pr_info("%s: rid:%d vbus:%d\n", __func__, rid, vbus);
+
+       switch (rid) {
+       case CCIC_RID_255K:
+       case CCIC_RID_301K:
+               if (pdata->jig_uart_cb)
+                       pdata->jig_uart_cb(1);
+               muic_manager_switch_path(muic_if, MUIC_PATH_USB_AP);
+               break;
+       case CCIC_RID_523K:
+       case CCIC_RID_619K:
+               if (pdata->jig_uart_cb)
+                       pdata->jig_uart_cb(1);
+               muic_manager_switch_path(muic_if, MUIC_PATH_UART_AP);
+               break;
+       default:
+               pr_info("%s: Unsupported rid\n", __func__);
+               return 0;
+       }
+
+       attached_dev = muic_manager_conv_rid_to_dev(muic_if, rid, vbus);
+
+       if (attached_dev != ccic->attached_dev) {
+               if (muic_manager_is_supported_dev(ccic->attached_dev)) {
+                       MUIC_SEND_NOTI_DETACH(ccic->attached_dev);
+                       ccic->attached_dev = 0;
+               } else if (muic_if->legacy_dev != ATTACHED_DEV_NONE_MUIC) {
+                       MUIC_SEND_NOTI_DETACH(muic_if->legacy_dev);
+                       muic_if->legacy_dev = 0;
+               }
+
+               ccic->attached_dev = attached_dev;
+               muic_if->set_cable_state(muic_if->muic_data, ccic->attached_dev);
+               MUIC_SEND_NOTI_ATTACH(attached_dev);
+       }
+
+       return 0;
+}
+
+static int muic_manager_handle_ccic_rid(struct muic_interface_t *muic_if, void *data)
+{
+       struct ccic_desc_t *ccic = muic_if->ccic;
+       struct muic_platform_data *pdata = muic_if->pdata;
+       int rid, vbus;
+#ifdef CONFIG_IFCONN_NOTIFIER
+       struct ifconn_notifier_template *pnoti =
+           (struct ifconn_notifier_template *)data;
+#else
+       CC_NOTI_RID_TYPEDEF *pnoti = (CC_NOTI_RID_TYPEDEF *) data;
+#endif
+
+       pr_info("%s: src:%d dest:%d id:%d rid:%d sub2:%d sub3:%d\n", __func__,
+               pnoti->src, pnoti->dest, pnoti->id, pnoti->rid, pnoti->sub2,
+               pnoti->sub3);
+
+       rid = pnoti->rid;
+
+       if (rid > CCIC_RID_OPEN) {
+               pr_info("%s: Out of range of RID\n", __func__);
+               return 0;
+       }
+
+       if (ccic->ccic_evt_attached != MUIC_CCIC_NOTI_ATTACH) {
+               pr_info("%s: RID but No ATTACH->discarded\n", __func__);
+               return 0;
+       }
+
+       ccic->ccic_evt_rid = rid;
+
+       switch (rid) {
+       case CCIC_RID_000K:
+               pr_info("%s: OTG -> RID000K\n", __func__);
+               muic_manager_switch_path(muic_if, MUIC_PATH_USB_AP);
+               vbus = muic_manager_get_vbus(muic_if);
+               ccic->attached_dev = muic_manager_conv_rid_to_dev(muic_if, rid, vbus);
+               return 0;
+       case CCIC_RID_001K:
+               pr_info("%s: MHL -> discarded.\n", __func__);
+               return 0;
+       case CCIC_RID_255K:
+       case CCIC_RID_301K:
+       case CCIC_RID_523K:
+       case CCIC_RID_619K:
+               vbus = muic_manager_get_vbus(muic_if);
+               muic_if->set_jig_state(muic_if->muic_data, true);
+               muic_manager_handle_ccic_factory_jig(muic_if, rid, vbus);
+               break;
+       case CCIC_RID_OPEN:
+       case CCIC_RID_UNDEFINED:
+               vbus = muic_manager_get_vbus(muic_if);
+               if (ccic->ccic_evt_attached == MUIC_CCIC_NOTI_ATTACH &&
+                   muic_manager_is_valid_rid_open(muic_if, vbus)) {
+                       if (pdata->jig_uart_cb)
+                               pdata->jig_uart_cb(0);
+                       /*
+                        * USB team's requirement.
+                        * Set AP USB for enumerations.
+                        */
+                       muic_manager_switch_path(muic_if, MUIC_PATH_USB_AP);
+                       muic_manager_handle_legacy_dev(muic_if);
+               } else {
+                       /* RID OPEN + No VBUS = Assume detach */
+                       muic_manager_handle_ccic_detach(muic_if);
+               }
+               muic_if->set_jig_state(muic_if->muic_data, false);
+               break;
+       default:
+               pr_err("%s:Undefined RID\n", __func__);
+               return 0;
+       }
+
+       return 0;
+}
+
+static int muic_manager_handle_ccic_water(struct muic_interface_t *muic_if, void *data)
+{
+#ifdef CONFIG_IFCONN_NOTIFIER
+       struct ifconn_notifier_template *pnoti = (struct ifconn_notifier_template *)data;
+#else
+       CC_NOTI_ATTACH_TYPEDEF *pnoti = (CC_NOTI_ATTACH_TYPEDEF *) data;
+#endif
+
+       pr_info("%s: src:%d dest:%d id:%d attach:%d cable_type:%d rprd:%d\n", __func__,
+               pnoti->src, pnoti->dest, pnoti->id, pnoti->attach, pnoti->cable_type, pnoti->rprd);
+
+       muic_if->afc_water_disable = pnoti->attach ? true : false;
+       muic_if->set_water_detect(muic_if->muic_data, muic_if->afc_water_disable);
+
+       pr_info("%s: Water detect : %s\n", __func__, pnoti->attach ? "en":"dis");
+
+       return 0;
+}
+
+static int muic_manager_handle_ccic_TA(struct muic_interface_t *muic_if, void *data)
+{
+#ifdef CONFIG_IFCONN_NOTIFIER
+       struct ifconn_notifier_template *pnoti = (struct ifconn_notifier_template *)data;
+#else
+       CC_NOTI_ATTACH_TYPEDEF *pnoti = (CC_NOTI_ATTACH_TYPEDEF *) data;
+#endif
+
+       pr_info("%s: src:%d dest:%d id:%d attach:%d cable_type:%d rprd:%d\n", __func__,
+               pnoti->src, pnoti->dest, pnoti->id, pnoti->attach, pnoti->cable_type, pnoti->rprd);
+
+       return 0;
+}
+
+static int muic_manager_handle_otg(struct muic_interface_t *muic_if, void *data)
+{
+       int ret = 0;
+       struct ccic_desc_t *ccic = muic_if->ccic;
+#ifdef CONFIG_IFCONN_NOTIFIER
+       struct ifconn_notifier_template *pnoti = (struct ifconn_notifier_template *)data;
+#else
+       CC_NOTI_TYPEDEF *pnoti = (CC_NOTI_TYPEDEF *) data;
+#endif
+
+       pr_info("%s: src:%d dest:%d id:%d\n", __func__,
+               pnoti->src, pnoti->dest, pnoti->id);
+
+       /* OTG Attach */
+       if (ret == MUIC_NORMAL_OTG) {
+               MUIC_SEND_NOTI_TO_CCIC_ATTACH(ATTACHED_DEV_OTG_MUIC);
+               ccic->ccic_evt_rprd = 1;
+               ccic->attached_dev = ATTACHED_DEV_OTG_MUIC;
+               muic_if->set_cable_state(muic_if->muic_data, ccic->attached_dev);
+               muic_manager_switch_path(muic_if, MUIC_PATH_USB_AP);
+       }
+#if 0
+       muic_if->set_otg_detect_en(muic_if->muic_data, pnoti->sub2 ? true:false);
+       pr_info("%s: set_otg_detect_en : %s\n", __func__, pnoti->sub2 ? "en":"dis");
+#endif
+
+       return 0;
+}
+
+static int muic_manager_handle_ccic_pd_charger(struct muic_interface_t *muic_if, void *data)
+{
+       struct ccic_desc_t *ccic = muic_if->ccic;
+#ifdef CONFIG_IFCONN_NOTIFIER
+       struct ifconn_notifier_template *pnoti = (struct ifconn_notifier_template *)data;
+#else
+       CC_NOTI_ATTACH_TYPEDEF *pnoti = (CC_NOTI_ATTACH_TYPEDEF *) data;
+#endif
+
+       pr_info("%s: src:%d dest:%d id:%d\n", __func__,
+               pnoti->src, pnoti->dest, pnoti->id);
+
+       ccic->attached_dev = ATTACHED_DEV_TYPE3_CHARGER_MUIC;
+       muic_if->set_cable_state(muic_if->muic_data, ccic->attached_dev);
+
+       return 0;
+}
+
+static int muic_manager_handle_notification(struct notifier_block *nb,
+                                           unsigned long action, void *data)
+{
+#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER
+       struct muic_interface_t *muic_if =
+           container_of(nb, struct muic_interface_t, manager_nb);
+#else
+       struct muic_interface_t *muic_if =
+           container_of(nb, struct muic_interface_t, nb);
+#endif
+
+#ifdef CONFIG_IFCONN_NOTIFIER
+       struct ifconn_notifier_template *pnoti =
+           (struct ifconn_notifier_template *)data;
+       int attach = IFCONN_NOTIFY_ID_ATTACH;
+       int rid = IFCONN_NOTIFY_ID_RID;
+       int water = IFCONN_NOTIFY_ID_WATER;
+       int otg = IFCONN_NOTIFY_ID_OTG;
+       int ta = IFCONN_NOTIFY_ID_TA;
+       int pd = IFCONN_NOTIFY_ID_POWER_STATUS;
+#else
+       CC_NOTI_TYPEDEF *pnoti = (CC_NOTI_TYPEDEF *) data;
+       int attach = CCIC_NOTIFY_ID_ATTACH;
+       int rid = CCIC_NOTIFY_ID_RID;
+       int water = CCIC_NOTIFY_ID_WATER;
+       int otg = CCIC_NOTIFY_ID_OTG;
+       int ta = CCIC_NOTIFY_ID_TA;
+#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER
+       if (pnoti->dest != CCIC_NOTIFY_DEV_MUIC) {
+               pr_info("%s destination id is invalid\n", __func__);
+               return 0;
+       }
+#endif
+#endif
+       muic_manager_show_status(muic_if);
+
+       if (pnoti->id == attach) {
+               pr_info("%s: NOTIFY_ID_ATTACH: %s\n", __func__,
+                       pnoti->sub1 ? "Attached" : "Detached");
+               muic_manager_handle_ccic_attach(muic_if, data);
+       } else if (pnoti->id == rid) {
+               pr_info("%s: NOTIFY_ID_RID\n", __func__);
+               muic_manager_handle_ccic_rid(muic_if, data);
+       } else if (pnoti->id == water) {
+               pr_info("%s: NOTIFY_ID_WATER\n", __func__);
+               muic_manager_handle_ccic_water(muic_if, data);
+       } else if (pnoti->id == otg) {
+               pr_info("%s: NOTIFY_ID_OTG\n", __func__);
+               muic_manager_handle_otg(muic_if, data);
+       } else if (pnoti->id == ta) {
+               pr_info("%s: NOTIFY_ID_TA\n", __func__);
+               muic_manager_handle_ccic_TA(muic_if, data);
+       } else if (pnoti->id == pd) {
+               pr_info("%s: NOTIFY_ID_TA\n", __func__);
+               muic_manager_handle_ccic_pd_charger(muic_if, data);
+       } else {
+               pr_info("%s: Undefined Noti. ID\n", __func__);
+       }
+
+       muic_manager_show_status(muic_if);
+
+       return NOTIFY_DONE;
+}
+
+#ifndef CONFIG_IFCONN_NOTIFIER
+void _muic_delayed_notifier(struct work_struct *work)
+{
+       struct muic_interface_t *muic_if;
+       int ret = 0;
+
+       pr_info("%s\n", __func__);
+
+       muic_if = container_of(work, struct muic_interface_t, ccic_work.work);
+
+#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER
+       ret = manager_notifier_register(&muic_if->manager_nb,
+                                       muic_manager_handle_notification,
+                                       MANAGER_NOTIFY_CCIC_MUIC);
+#else
+       ret = ccic_notifier_register(&muic_if->ccic_nb,
+                                    muic_manager_handle_notification,
+                                    CCIC_NOTIFY_DEV_MUIC);
+#endif
+
+       if (ret < 0) {
+               pr_info("%s: CCIC Noti. is not ready. Try again in 4sec...\n", __func__);
+               schedule_delayed_work(&muic_if->ccic_work, msecs_to_jiffies(4000));
+               return;
+       }
+
+       pr_info("%s: done.\n", __func__);
+}
+
+void muic_manager_register_notifier(struct muic_interface_t *muic_if)
+{
+       int ret = 0;
+
+       pr_info("%s: Registering CCIC_NOTIFY_DEV_MUIC.\n", __func__);
+
+       muic_manager_init_dev_desc(muic_if);
+
+#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER
+       ret = manager_notifier_register(&muic_if->manager_nb,
+                                       muic_manager_handle_notification,
+                                       MANAGER_NOTIFY_CCIC_MUIC);
+#else
+       ret = ccic_notifier_register(&muic_if->ccic_nb,
+                                    muic_manager_handle_notification,
+                                    CCIC_NOTIFY_DEV_MUIC);
+#endif
+
+       if (ret < 0) {
+               pr_info("%s: CCIC Noti. is not ready. Try again in 8sec...\n", __func__);
+               INIT_DELAYED_WORK(&muic_if->ccic_work, _muic_delayed_notifier);
+               schedule_delayed_work(&muic_if->ccic_work, msecs_to_jiffies(8000));
+               return;
+       }
+
+       pr_info("%s: done.\n", __func__);
+}
+#endif
+
+struct muic_interface_t *muic_manager_init(void *pdata, void *drv_data)
+{
+#ifdef CONFIG_IFCONN_NOTIFIER
+       int ret;
+#endif
+       struct muic_interface_t *muic_if;
+       struct ccic_desc_t *ccic;
+
+       pr_info("%s\n", __func__);
+
+       muic_if = kzalloc(sizeof(*muic_if), GFP_KERNEL);
+       if (unlikely(!muic_if)) {
+               pr_err("%s failed to allocate driver data\n", __func__);
+               return NULL;
+       }
+
+       ccic = kzalloc(sizeof(*ccic), GFP_KERNEL);
+       if (unlikely(!ccic)) {
+               pr_err("%s failed to allocate driver data\n", __func__);
+               goto err_ccic_alloc;
+       }
+
+       muic_if->ccic = ccic;
+       muic_if->muic_data = drv_data;
+       muic_if->pdata = pdata;
+       muic_if->ccic->rid_desc = ccic_rid_tbl;
+       muic_if->is_afc_reset = false;
+       muic_if->is_dcp_charger = false;
+       muic_if->opmode = get_ccic_info() & 0xF;
+       muic_if->is_dcdtmr_intr = false;
+#ifdef CONFIG_IFCONN_NOTIFIER
+       ret = ifconn_notifier_register(&muic_if->nb,
+                                      muic_manager_handle_notification,
+                                      IFCONN_NOTIFY_MUIC, IFCONN_NOTIFY_CCIC);
+       ret = ifconn_notifier_register(&muic_if->nb,
+                                      muic_manager_handle_notification,
+                                      IFCONN_NOTIFY_MUIC, IFCONN_NOTIFY_PDIC);
+       if (ret) {
+               pr_err("%s failed register ifconn notifier\n", __func__);
+               goto err_reg_noti;
+       }
+#else
+       if (muic_if->opmode & OPMODE_DEVICE)
+               muic_manager_register_notifier(muic_if);
+       else
+               pr_info("OPMODE_MUIC CCIC NOTIFIER is not used\n");
+#endif
+       return muic_if;
+#ifdef CONFIG_IFCONN_NOTIFIER
+err_reg_noti:
+       kfree(ccic);
+#endif
+err_ccic_alloc:
+       kfree(muic_if);
+       return NULL;
+}
+
+void muic_manager_exit(struct muic_interface_t *muic_if)
+{
+       kfree(muic_if);
+}
diff --git a/drivers/muic/muic_notifier.c b/drivers/muic/muic_notifier.c
new file mode 100644 (file)
index 0000000..4825f14
--- /dev/null
@@ -0,0 +1,213 @@
+#include <linux/device.h>
+
+#include <linux/notifier.h>
+#include <linux/muic/muic.h>
+#include <linux/muic/muic_notifier.h>
+#include <linux/sec_sysfs.h>
+
+/*
+  * The src & dest addresses of the noti.
+  * keep the same value defined in ccic_notifier.h
+  *     b'0001 : CCIC
+  *     b'0010 : MUIC
+  *     b'1111 : Broadcasting
+  */
+#define NOTI_ADDR_SRC (1 << 1)
+#define NOTI_ADDR_DST (0xf)
+
+/* ATTACH Noti. ID */
+#define NOTI_ID_ATTACH (1)
+
+
+#define SET_MUIC_NOTIFIER_BLOCK(nb, fn, dev) do {      \
+               (nb)->notifier_call = (fn);             \
+               (nb)->priority = (dev);                 \
+       } while (0)
+
+#define DESTROY_MUIC_NOTIFIER_BLOCK(nb)                        \
+               SET_MUIC_NOTIFIER_BLOCK(nb, NULL, -1)
+
+static struct muic_notifier_struct muic_notifier;
+
+struct device *switch_device;
+
+static int muic_uses_new_noti;
+
+void muic_notifier_set_new_noti(bool flag)
+{
+       muic_uses_new_noti = flag ? 1: 0;
+}
+
+static void __set_noti_cxt(int attach, int type)
+{
+       if (type < 0) {
+               muic_notifier.cmd = attach;
+               muic_notifier.cxt.attach = attach;
+               return;
+       }
+
+       /* Old Interface */
+       muic_notifier.cmd = attach;
+       muic_notifier.attached_dev = type;
+
+       /* New Interface */
+       muic_notifier.cxt.src = NOTI_ADDR_SRC;
+       muic_notifier.cxt.dest = NOTI_ADDR_DST;
+       muic_notifier.cxt.id = NOTI_ID_ATTACH;
+       muic_notifier.cxt.attach = attach;
+       muic_notifier.cxt.cable_type = type;
+       muic_notifier.cxt.rprd = 0;
+}
+
+int muic_notifier_register(struct notifier_block *nb, notifier_fn_t notifier,
+                       muic_notifier_device_t listener)
+{
+       int ret = 0;
+       void *pcxt;;
+
+       pr_info("%s: listener=%d register\n", __func__, listener);
+
+       SET_MUIC_NOTIFIER_BLOCK(nb, notifier, listener);
+       ret = blocking_notifier_chain_register(&(muic_notifier.notifier_call_chain), nb);
+       if (ret < 0)
+               pr_err("%s: blocking_notifier_chain_register error(%d)\n",
+                               __func__, ret);
+
+       pcxt = muic_uses_new_noti ? &(muic_notifier.cxt) : \
+                       (void *)&(muic_notifier.attached_dev);
+
+       /* current muic's attached_device status notify */
+       nb->notifier_call(nb, muic_notifier.cxt.attach, pcxt);
+
+       return ret;
+}
+
+int muic_notifier_unregister(struct notifier_block *nb)
+{
+       int ret = 0;
+
+       pr_info("%s: listener=%d unregister\n", __func__, nb->priority);
+
+       ret = blocking_notifier_chain_unregister(&(muic_notifier.notifier_call_chain), nb);
+       if (ret < 0)
+               pr_err("%s: blocking_notifier_chain_unregister error(%d)\n",
+                               __func__, ret);
+       DESTROY_MUIC_NOTIFIER_BLOCK(nb);
+
+       return ret;
+}
+
+static int muic_notifier_notify(void)
+{
+       int ret = 0;
+       void *pcxt;
+
+       pr_info("%s: CMD=%d, DATA=%d\n", __func__, muic_notifier.cxt.attach,
+                       muic_notifier.cxt.cable_type);
+
+#ifdef CONFIG_SEC_FACTORY
+       if (muic_notifier.cxt.attach != 0)
+               muic_send_attached_muic_cable_intent(muic_notifier.cxt.cable_type);
+       else
+               muic_send_attached_muic_cable_intent(0);
+#endif
+       pcxt = muic_uses_new_noti ? &(muic_notifier.cxt) : \
+                       (void *)&(muic_notifier.attached_dev);
+
+       ret = blocking_notifier_call_chain(&(muic_notifier.notifier_call_chain),
+                       muic_notifier.cxt.attach, pcxt);
+
+       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;
+}
+
+void muic_notifier_attach_attached_dev(muic_attached_dev_t new_dev)
+{
+       pr_info("%s: (%d)\n", __func__, new_dev);
+
+       __set_noti_cxt(MUIC_NOTIFY_CMD_ATTACH, new_dev);
+
+       /* muic's attached_device attach broadcast */
+       muic_notifier_notify();
+}
+
+void muic_notifier_detach_attached_dev(muic_attached_dev_t cur_dev)
+{
+       pr_info("%s: (%d)\n", __func__, cur_dev);
+
+       __set_noti_cxt(MUIC_NOTIFY_CMD_DETACH, -1);
+
+       if (muic_notifier.cxt.cable_type != cur_dev)
+               pr_warn("%s: attached_dev of muic_notifier(%d) != muic_data(%d)\n",
+                               __func__, muic_notifier.cxt.cable_type, cur_dev);
+
+       if (muic_notifier.cxt.cable_type != ATTACHED_DEV_NONE_MUIC) {
+               /* muic's attached_device detach broadcast */
+               muic_notifier_notify();
+       }
+
+       __set_noti_cxt(0, ATTACHED_DEV_NONE_MUIC);
+}
+
+void muic_notifier_logically_attach_attached_dev(muic_attached_dev_t new_dev)
+{
+       pr_info("%s: (%d)\n", __func__, new_dev);
+
+       __set_noti_cxt(MUIC_NOTIFY_CMD_ATTACH, new_dev);
+
+       /* muic's attached_device attach broadcast */
+       muic_notifier_notify();
+}
+
+void muic_notifier_logically_detach_attached_dev(muic_attached_dev_t cur_dev)
+{
+       pr_info("%s: (%d)\n", __func__, cur_dev);
+
+       __set_noti_cxt(MUIC_NOTIFY_CMD_DETACH, cur_dev);
+
+       /* muic's attached_device detach broadcast */
+       muic_notifier_notify();
+
+       __set_noti_cxt(0, ATTACHED_DEV_NONE_MUIC);
+}
+
+static int __init muic_notifier_init(void)
+{
+       int ret = 0;
+
+       pr_info("%s\n", __func__);
+#if defined(CONFIG_MUIC_SUPPORT_CCIC) && \
+               defined(CONFIG_CCIC_NOTIFIER)
+       muic_uses_new_noti = 1;
+
+#endif
+
+       switch_device = sec_device_create(NULL, "switch");
+       if (IS_ERR(switch_device)) {
+               pr_err("%s Failed to create device(switch)!\n", __func__);
+               ret = -ENODEV;
+               goto out;
+       }
+
+       BLOCKING_INIT_NOTIFIER_HEAD(&(muic_notifier.notifier_call_chain));
+       __set_noti_cxt(0 ,ATTACHED_DEV_UNKNOWN_MUIC);
+
+out:
+       return ret;
+}
+
+device_initcall(muic_notifier_init);
+
diff --git a/drivers/muic/muic_sysfs.c b/drivers/muic/muic_sysfs.c
new file mode 100644 (file)
index 0000000..db07477
--- /dev/null
@@ -0,0 +1,49 @@
+#include <linux/device.h>
+#include <linux/err.h>
+
+static struct class *muic_class;
+static atomic_t muic_dev;
+
+static int __init muic_class_create(void)
+{
+       muic_class = class_create(THIS_MODULE, "sec");
+       if (IS_ERR(muic_class)) {
+               pr_err("Failed to create class(muic) %ld\n", PTR_ERR(muic_class));
+               return PTR_ERR(muic_class);
+       }
+       return 0;
+}
+
+struct device *muic_device_create(void *drvdata, const char *fmt)
+{
+       struct device *dev;
+
+       if (IS_ERR(muic_class)) {
+               pr_err("Failed to create class(muic) %ld\n", PTR_ERR(muic_class));
+               BUG();
+       }
+
+       if (!muic_class) {
+               pr_err("Not yet created class(muic)!\n");
+               BUG();
+       }
+
+       dev = device_create(muic_class, NULL, atomic_inc_return(&muic_dev),
+                       drvdata, fmt);
+       if (IS_ERR(dev))
+               pr_err("Failed to create device %s %ld\n", fmt, PTR_ERR(dev));
+       else
+               pr_debug("%s : %s : %d\n", __func__, fmt, dev->devt);
+
+       return dev;
+}
+EXPORT_SYMBOL(muic_device_create);
+
+void muic_device_destroy(dev_t devt)
+{
+       pr_info("%s : %d\n", __func__, devt);
+       device_destroy(muic_class, devt);
+}
+EXPORT_SYMBOL(muic_device_destroy);
+
+subsys_initcall(muic_class_create);
diff --git a/drivers/muic/s2mu004-muic-hv.c b/drivers/muic/s2mu004-muic-hv.c
new file mode 100644 (file)
index 0000000..2475d2d
--- /dev/null
@@ -0,0 +1,1618 @@
+/*
+ * s2mu004-muic.c - MUIC driver for the Samsung s2mu004
+ *
+ *  Copyright (C) 2015 Samsung Electronics
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * This driver is based on max77843-muic-afc.c
+ *
+ */
+
+#define pr_fmt(fmt)    "[MUIC_HV] " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/workqueue.h>
+#include <linux/wakelock.h>
+
+#include <linux/mfd/samsung/s2mu004.h>
+#include <linux/mfd/samsung/s2mu004-private.h>
+
+/* MUIC header file */
+#include <linux/muic/muic_core.h>
+#include <linux/muic/s2mu004-muic.h>
+#include <linux/muic/s2mu004-muic-hv.h>
+#ifdef CONFIG_MUIC_MANAGER
+#include <linux/muic/muic_interface.h>
+#endif
+
+#if defined(CONFIG_MUIC_NOTIFIER)
+#include <linux/muic/s2mu004-muic-notifier.h>
+#endif /* CONFIG_MUIC_NOTIFIER */
+#include <linux/delay.h>
+
+static bool debug_en_checklist = true;
+
+enum act_function_num {
+       FUNC_TA_TO_PREPARE,
+       FUNC_PREPARE_TO_PREPARE_DUPLI,
+       FUNC_PREPARE_TO_AFC_5V,
+       FUNC_PREPARE_TO_QC_PREPARE,
+       FUNC_PREPARE_DUPLI_TO_PREPARE_DUPLI,
+       FUNC_PREPARE_DUPLI_TO_AFC_5V,
+       FUNC_PREPARE_DUPLI_TO_AFC_ERR_V,
+       FUNC_PREPARE_DUPLI_TO_AFC_9V,
+       FUNC_AFC_5V_TO_AFC_5V_DUPLI,
+       FUNC_AFC_5V_TO_AFC_ERR_V,
+       FUNC_AFC_5V_TO_AFC_9V,
+       FUNC_AFC_5V_DUPLI_TO_AFC_5V_DUPLI,
+       FUNC_AFC_5V_DUPLI_TO_AFC_ERR_V,
+       FUNC_AFC_5V_DUPLI_TO_AFC_9V,
+       FUNC_AFC_ERR_V_TO_AFC_ERR_V_DUPLI,
+       FUNC_AFC_ERR_V_TO_AFC_9V,
+       FUNC_AFC_ERR_V_DUPLI_TO_AFC_ERR_V_DUPLI,
+       FUNC_AFC_ERR_V_DUPLI_TO_AFC_9V,
+       FUNC_AFC_9V_TO_AFC_5V,
+       FUNC_AFC_9V_TO_AFC_9V,
+       FUNC_QC_PREPARE_TO_QC_9V,
+       FUNC_QC_9V_TO_QC_5V,
+};
+
+muic_afc_data_t prepare_dupli_to_afc_9v;
+/* afc_condition_checklist[ATTACHED_DEV_TA_MUIC] */
+muic_afc_data_t ta_to_prepare = {
+       .new_dev                = ATTACHED_DEV_AFC_CHARGER_PREPARE_MUIC,
+       .afc_name               = "HV charger Prepare",
+       .afc_irq                = MUIC_AFC_IRQ_VDNMON,
+       .status_vbadc           = VBADC_DONTCARE,
+       .status_vdnmon          = VDNMON_LOW,
+       .function_num           = FUNC_TA_TO_PREPARE,
+       .next                   = &ta_to_prepare,
+};
+
+/* afc_condition_checklist[ATTACHED_DEV_AFC_CHARGER_PREPARE_MUIC] */
+muic_afc_data_t prepare_to_qc_prepare = {
+       .new_dev                = ATTACHED_DEV_QC_CHARGER_PREPARE_MUIC,
+       .afc_name               = "QC charger Prepare",
+       .afc_irq                = MUIC_AFC_IRQ_MPNACK,
+       .status_vbadc           = VBADC_DONTCARE,
+       .status_vdnmon          = VDNMON_DONTCARE,
+       .function_num           = FUNC_PREPARE_TO_QC_PREPARE,
+       .next                   = &prepare_dupli_to_afc_9v,
+};
+
+muic_afc_data_t prepare_to_afc_5v = {
+       .new_dev                = ATTACHED_DEV_AFC_CHARGER_5V_MUIC,
+       .afc_name               = "AFC charger 5V",
+       .afc_irq                = MUIC_AFC_IRQ_VBADC,
+       .status_vbadc           = VBADC_AFC_5V,
+       .status_vdnmon          = VDNMON_DONTCARE,
+       .function_num           = FUNC_PREPARE_TO_AFC_5V,
+       .next                   = &prepare_to_afc_5v,
+};
+
+muic_afc_data_t prepare_to_prepare_dupli = {
+       .new_dev                = ATTACHED_DEV_AFC_CHARGER_PREPARE_DUPLI_MUIC,
+       .afc_name               = "AFC charger prepare (mrxrdy)",
+       .afc_irq                = MUIC_AFC_IRQ_MRXRDY,
+       .status_vbadc           = VBADC_DONTCARE,
+       .status_vdnmon          = VDNMON_DONTCARE,
+       .function_num           = FUNC_PREPARE_TO_PREPARE_DUPLI,
+       .next                   = &prepare_to_qc_prepare,
+};
+
+muic_afc_data_t prepare_dupli_to_prepare_dupli = {
+       .new_dev                = ATTACHED_DEV_AFC_CHARGER_PREPARE_DUPLI_MUIC,
+       .afc_name               = "AFC charger prepare (mrxrdy)",
+       .afc_irq                = MUIC_AFC_IRQ_MRXRDY,
+       .status_vbadc           = VBADC_DONTCARE,
+       .status_vdnmon          = VDNMON_DONTCARE,
+       .function_num           = FUNC_PREPARE_DUPLI_TO_PREPARE_DUPLI,
+       .next                   = &prepare_to_qc_prepare,
+};
+
+muic_afc_data_t prepare_dupli_to_afc_early_9v = {
+       .new_dev                = ATTACHED_DEV_AFC_CHARGER_9V_MUIC,
+       .afc_name               = "AFC charger 9V (early in mrxrdy)",
+       .afc_irq                = MUIC_AFC_IRQ_MRXRDY,
+       .status_vbadc           = VBADC_AFC_9V,
+       .status_vdnmon          = VDNMON_DONTCARE,
+       .function_num           = FUNC_PREPARE_DUPLI_TO_AFC_9V,
+       .next                   = &prepare_dupli_to_prepare_dupli,
+};
+
+muic_afc_data_t prepare_dupli_to_afc_9v = {
+       .new_dev                = ATTACHED_DEV_AFC_CHARGER_9V_MUIC,
+       .afc_name               = "AFC charger 9V",
+       .afc_irq                = MUIC_AFC_IRQ_VBADC,
+       .status_vbadc           = VBADC_AFC_9V,
+       .status_vdnmon          = VDNMON_DONTCARE,
+       .function_num           = FUNC_PREPARE_DUPLI_TO_AFC_9V,
+       .next                   = &prepare_dupli_to_afc_early_9v,
+};
+
+muic_afc_data_t prepare_dupli_to_afc_err_v = {
+       .new_dev                = ATTACHED_DEV_AFC_CHARGER_ERR_V_MUIC,
+       .afc_name               = "AFC charger ERR V",
+       .afc_irq                = MUIC_AFC_IRQ_VBADC,
+       .status_vbadc           = VBADC_AFC_ERR_V,
+       .status_vdnmon          = VDNMON_DONTCARE,
+       .function_num           = FUNC_PREPARE_DUPLI_TO_AFC_ERR_V,
+       .next                   = &prepare_dupli_to_afc_9v,
+};
+
+muic_afc_data_t prepare_dupli_to_afc_5v = {
+       .new_dev                = ATTACHED_DEV_AFC_CHARGER_5V_MUIC,
+       .afc_name               = "AFC charger 5V",
+       .afc_irq                = MUIC_AFC_IRQ_VBADC,
+       .status_vbadc           = VBADC_AFC_5V,
+       .status_vdnmon          = VDNMON_DONTCARE,
+       .function_num           = FUNC_PREPARE_DUPLI_TO_AFC_5V,
+       .next                   = &prepare_dupli_to_afc_err_v,
+};
+
+muic_afc_data_t afc_5v_to_afc_9v = {
+       .new_dev                = ATTACHED_DEV_AFC_CHARGER_5V_MUIC,
+       .afc_name               = "AFC charger 9V",
+       .afc_irq                = MUIC_AFC_IRQ_VBADC,
+       .status_vbadc           = VBADC_AFC_5V,
+       .status_vdnmon          = VDNMON_DONTCARE,
+       .function_num           = FUNC_AFC_5V_TO_AFC_9V,
+       .next                   = &afc_5v_to_afc_9v,
+};
+
+muic_afc_data_t afc_5v_to_afc_err_v = {
+       .new_dev                = ATTACHED_DEV_AFC_CHARGER_ERR_V_MUIC,
+       .afc_name               = "AFC charger ERR V",
+       .afc_irq                = MUIC_AFC_IRQ_VBADC,
+       .status_vbadc           = VBADC_AFC_ERR_V,
+       .status_vdnmon          = VDNMON_DONTCARE,
+       .function_num           = FUNC_AFC_5V_TO_AFC_ERR_V,
+       .next                   = &afc_5v_to_afc_9v,
+};
+
+muic_afc_data_t afc_5v_to_afc_5v_dupli = {
+       .new_dev                = ATTACHED_DEV_AFC_CHARGER_PREPARE_DUPLI_MUIC,
+       .afc_name               = "AFC charger 5V (mrxrdy)",
+       .afc_irq                = MUIC_AFC_IRQ_MRXRDY,
+       .status_vbadc           = VBADC_AFC_5V,
+       .status_vdnmon          = VDNMON_DONTCARE,
+       .function_num           = FUNC_AFC_5V_TO_AFC_5V_DUPLI,
+       .next                   = &prepare_dupli_to_prepare_dupli,
+};
+
+muic_afc_data_t afc_5v_dupli_to_afc_5v_dupli = {
+       .new_dev                = ATTACHED_DEV_AFC_CHARGER_5V_DUPLI_MUIC,
+       .afc_name               = "AFC charger 5V (mrxrdy)",
+       .afc_irq                = MUIC_AFC_IRQ_MRXRDY,
+       .status_vbadc           = VBADC_AFC_5V,
+       .status_vdnmon          = VDNMON_DONTCARE,
+       .function_num           = FUNC_AFC_5V_DUPLI_TO_AFC_5V_DUPLI,
+       .next                   = &afc_5v_dupli_to_afc_5v_dupli,
+};
+
+muic_afc_data_t afc_5v_dupli_to_afc_9v = {
+       .new_dev                = ATTACHED_DEV_AFC_CHARGER_9V_MUIC,
+       .afc_name               = "AFC charger 9V",
+       .afc_irq                = MUIC_AFC_IRQ_VBADC,
+       .status_vbadc           = VBADC_AFC_9V,
+       .status_vdnmon          = VDNMON_DONTCARE,
+       .function_num           = FUNC_AFC_5V_DUPLI_TO_AFC_9V,
+       .next                   = &afc_5v_dupli_to_afc_5v_dupli,
+};
+
+muic_afc_data_t afc_5v_dupli_to_afc_err_v = {
+       .new_dev                = ATTACHED_DEV_AFC_CHARGER_ERR_V_MUIC,
+       .afc_name               = "AFC charger ERR V",
+       .afc_irq                = MUIC_AFC_IRQ_VBADC,
+       .status_vbadc           = VBADC_AFC_ERR_V,
+       .status_vdnmon          = VDNMON_DONTCARE,
+       .function_num           = FUNC_AFC_5V_DUPLI_TO_AFC_ERR_V,
+       .next                   = &afc_5v_dupli_to_afc_9v,
+};
+
+muic_afc_data_t afc_err_v_to_afc_9v = {
+       .new_dev                = ATTACHED_DEV_AFC_CHARGER_9V_MUIC,
+       .afc_name               = "AFC charger 9V",
+       .afc_irq                = MUIC_AFC_IRQ_VBADC,
+       .status_vbadc           = VBADC_AFC_9V,
+       .status_vdnmon          = VDNMON_DONTCARE,
+       .function_num           = FUNC_AFC_ERR_V_TO_AFC_9V,
+       .next                   = &afc_err_v_to_afc_9v,
+};
+
+muic_afc_data_t afc_err_v_to_afc_err_v_dupli = {
+       .new_dev                = ATTACHED_DEV_AFC_CHARGER_ERR_V_DUPLI_MUIC,
+       .afc_name               = "AFC charger ERR V (mrxrdy)",
+       .afc_irq                = MUIC_AFC_IRQ_MRXRDY,
+       .status_vbadc           = VBADC_AFC_ERR_V,
+       .status_vdnmon          = VDNMON_DONTCARE,
+       .function_num           = FUNC_AFC_ERR_V_TO_AFC_ERR_V_DUPLI,
+       .next                   = &afc_err_v_to_afc_9v,
+};
+
+muic_afc_data_t afc_err_v_dupli_to_afc_9v = {
+       .new_dev                = ATTACHED_DEV_AFC_CHARGER_9V_MUIC,
+       .afc_name               = "AFC charger 9V",
+       .afc_irq                = MUIC_AFC_IRQ_VBADC,
+       .status_vbadc           = VBADC_AFC_9V,
+       .status_vdnmon          = VDNMON_DONTCARE,
+       .function_num           = FUNC_AFC_ERR_V_DUPLI_TO_AFC_9V,
+       .next                   = &afc_err_v_dupli_to_afc_9v,
+};
+
+muic_afc_data_t afc_err_v_dupli_to_afc_err_v_dupli = {
+       .new_dev                = ATTACHED_DEV_AFC_CHARGER_ERR_V_DUPLI_MUIC,
+       .afc_name               = "AFC charger ERR V (mrxrdy)",
+       .afc_irq                = MUIC_AFC_IRQ_MRXRDY,
+       .status_vbadc           = VBADC_AFC_ERR_V,
+       .status_vdnmon          = VDNMON_DONTCARE,
+       .function_num   = FUNC_AFC_ERR_V_DUPLI_TO_AFC_ERR_V_DUPLI,
+       .next                   = &afc_err_v_dupli_to_afc_9v,
+};
+
+muic_afc_data_t qc_prepare_to_qc_9v = {
+       .new_dev                = ATTACHED_DEV_QC_CHARGER_9V_MUIC,
+       .afc_name               = "QC charger 9V",
+       .afc_irq                = MUIC_AFC_IRQ_VBADC,
+       .status_vbadc           = VBADC_QC_9V,
+       .status_vdnmon          = VDNMON_DONTCARE,
+       .function_num           = FUNC_QC_PREPARE_TO_QC_9V,
+       .next                   = &qc_prepare_to_qc_9v,
+};
+
+muic_afc_data_t qc_9v_to_qc_9v = {
+       .new_dev                = ATTACHED_DEV_QC_CHARGER_9V_MUIC,
+       .afc_name               = "QC charger 9V",
+       .afc_irq                = MUIC_AFC_IRQ_DONTCARE,
+       .status_vbadc           = VBADC_QC_9V,
+       .status_vdnmon          = VDNMON_DONTCARE,
+       .function_num           = FUNC_QC_PREPARE_TO_QC_9V,
+       .next                   = &qc_9v_to_qc_9v,
+};
+
+/* afc_condition_checklist[ATTACHED_DEV_AFC_CHARGER_9V_MUIC] */
+muic_afc_data_t afc_9v_to_afc_5v = {
+       .new_dev                = ATTACHED_DEV_AFC_CHARGER_5V_MUIC,
+       .afc_name               = "AFC charger 5V",
+       .afc_irq                = MUIC_AFC_IRQ_VBADC,
+       .status_vbadc           = VBADC_AFC_5V,
+       .status_vdnmon          = VDNMON_DONTCARE,
+       .function_num           = FUNC_AFC_9V_TO_AFC_5V,
+       .next                   = &afc_5v_to_afc_5v_dupli,
+};
+
+/* afc_condition_checklist[ATTACHED_DEV_AFC_CHARGER_9V_MUIC] */
+muic_afc_data_t afc_9v_to_afc_9v = {
+       .new_dev                = ATTACHED_DEV_AFC_CHARGER_9V_MUIC,
+       .afc_name               = "AFC charger 9V",
+       .afc_irq                = MUIC_AFC_IRQ_DONTCARE,
+       .status_vbadc           = VBADC_AFC_9V,
+       .status_vdnmon          = VDNMON_DONTCARE,
+       .function_num           = FUNC_AFC_9V_TO_AFC_9V,
+       .next                   = &afc_9v_to_afc_5v,
+};
+
+muic_afc_data_t                *afc_condition_checklist[ATTACHED_DEV_NUM] = {
+       [ATTACHED_DEV_TA_MUIC]                  = &ta_to_prepare,
+       [ATTACHED_DEV_AFC_CHARGER_PREPARE_MUIC] = &prepare_to_prepare_dupli,
+       [ATTACHED_DEV_AFC_CHARGER_PREPARE_DUPLI_MUIC] = &prepare_dupli_to_afc_5v,
+       [ATTACHED_DEV_AFC_CHARGER_5V_MUIC]      = &afc_5v_to_afc_5v_dupli,
+       [ATTACHED_DEV_AFC_CHARGER_5V_DUPLI_MUIC] = &afc_5v_dupli_to_afc_err_v,
+       [ATTACHED_DEV_AFC_CHARGER_ERR_V_MUIC]   = &afc_err_v_to_afc_err_v_dupli,
+       [ATTACHED_DEV_AFC_CHARGER_ERR_V_DUPLI_MUIC] = &afc_err_v_dupli_to_afc_err_v_dupli,
+       [ATTACHED_DEV_AFC_CHARGER_9V_MUIC]      = &afc_9v_to_afc_9v,
+       [ATTACHED_DEV_QC_CHARGER_PREPARE_MUIC]  = &qc_prepare_to_qc_9v,
+       [ATTACHED_DEV_QC_CHARGER_9V_MUIC]       = &qc_9v_to_qc_9v,
+};
+
+struct afc_init_data_s {
+       struct work_struct muic_afc_init_work;
+       struct s2mu004_muic_data *muic_data;
+};
+struct afc_init_data_s afc_init_data;
+
+static const char *dev_to_str(muic_attached_dev_t n)
+{
+       switch (n) {
+               ENUM_STR(ATTACHED_DEV_NONE_MUIC);
+               ENUM_STR(ATTACHED_DEV_USB_MUIC);
+               ENUM_STR(ATTACHED_DEV_CDP_MUIC);
+               ENUM_STR(ATTACHED_DEV_OTG_MUIC);
+               ENUM_STR(ATTACHED_DEV_TA_MUIC);
+               ENUM_STR(ATTACHED_DEV_UNOFFICIAL_MUIC);
+               ENUM_STR(ATTACHED_DEV_UNOFFICIAL_TA_MUIC);
+               ENUM_STR(ATTACHED_DEV_UNOFFICIAL_ID_MUIC);
+               ENUM_STR(ATTACHED_DEV_UNOFFICIAL_ID_TA_MUIC);
+               ENUM_STR(ATTACHED_DEV_UNOFFICIAL_ID_ANY_MUIC);
+               ENUM_STR(ATTACHED_DEV_UNOFFICIAL_ID_USB_MUIC);
+               ENUM_STR(ATTACHED_DEV_UNOFFICIAL_ID_CDP_MUIC);
+               ENUM_STR(ATTACHED_DEV_UNDEFINED_CHARGING_MUIC);
+               ENUM_STR(ATTACHED_DEV_DESKDOCK_MUIC);
+               ENUM_STR(ATTACHED_DEV_UNKNOWN_VB_MUIC);
+               ENUM_STR(ATTACHED_DEV_DESKDOCK_VB_MUIC);
+               ENUM_STR(ATTACHED_DEV_CARDOCK_MUIC);
+               ENUM_STR(ATTACHED_DEV_JIG_UART_OFF_MUIC);
+               ENUM_STR(ATTACHED_DEV_JIG_UART_OFF_VB_MUIC);
+               ENUM_STR(ATTACHED_DEV_JIG_UART_OFF_VB_OTG_MUIC);
+               ENUM_STR(ATTACHED_DEV_JIG_UART_OFF_VB_FG_MUIC);
+               ENUM_STR(ATTACHED_DEV_JIG_UART_ON_MUIC);
+               ENUM_STR(ATTACHED_DEV_JIG_UART_ON_VB_MUIC);
+               ENUM_STR(ATTACHED_DEV_JIG_USB_OFF_MUIC);
+               ENUM_STR(ATTACHED_DEV_JIG_USB_ON_MUIC);
+               ENUM_STR(ATTACHED_DEV_JIG_RID_OPEN_MUIC);
+               ENUM_STR(ATTACHED_DEV_SMARTDOCK_MUIC);
+               ENUM_STR(ATTACHED_DEV_SMARTDOCK_VB_MUIC);
+               ENUM_STR(ATTACHED_DEV_SMARTDOCK_TA_MUIC);
+               ENUM_STR(ATTACHED_DEV_SMARTDOCK_USB_MUIC);
+               ENUM_STR(ATTACHED_DEV_UNIVERSAL_MMDOCK_MUIC);
+               ENUM_STR(ATTACHED_DEV_AUDIODOCK_MUIC);
+               ENUM_STR(ATTACHED_DEV_MHL_MUIC);
+               ENUM_STR(ATTACHED_DEV_CHARGING_CABLE_MUIC);
+               ENUM_STR(ATTACHED_DEV_AFC_CHARGER_PREPARE_MUIC);
+               ENUM_STR(ATTACHED_DEV_AFC_CHARGER_PREPARE_DUPLI_MUIC);
+               ENUM_STR(ATTACHED_DEV_AFC_CHARGER_5V_MUIC);
+               ENUM_STR(ATTACHED_DEV_AFC_CHARGER_5V_DUPLI_MUIC);
+               ENUM_STR(ATTACHED_DEV_AFC_CHARGER_9V_MUIC);
+               ENUM_STR(ATTACHED_DEV_AFC_CHARGER_ERR_V_MUIC);
+               ENUM_STR(ATTACHED_DEV_AFC_CHARGER_ERR_V_DUPLI_MUIC);
+               ENUM_STR(ATTACHED_DEV_QC_CHARGER_PREPARE_MUIC);
+               ENUM_STR(ATTACHED_DEV_QC_CHARGER_5V_MUIC);
+               ENUM_STR(ATTACHED_DEV_QC_CHARGER_ERR_V_MUIC);
+               ENUM_STR(ATTACHED_DEV_QC_CHARGER_9V_MUIC);
+               ENUM_STR(ATTACHED_DEV_HV_ID_ERR_UNDEFINED_MUIC);
+               ENUM_STR(ATTACHED_DEV_HV_ID_ERR_UNSUPPORTED_MUIC);
+               ENUM_STR(ATTACHED_DEV_HV_ID_ERR_SUPPORTED_MUIC);
+               ENUM_STR(ATTACHED_DEV_HMT_MUIC);
+               ENUM_STR(ATTACHED_DEV_VZW_ACC_MUIC);
+               ENUM_STR(ATTACHED_DEV_VZW_INCOMPATIBLE_MUIC);
+               ENUM_STR(ATTACHED_DEV_USB_LANHUB_MUIC);
+               ENUM_STR(ATTACHED_DEV_TYPE2_CHG_MUIC);
+               ENUM_STR(ATTACHED_DEV_TYPE3_MUIC);
+               ENUM_STR(ATTACHED_DEV_TYPE3_MUIC_TA);
+               ENUM_STR(ATTACHED_DEV_TYPE3_ADAPTER_MUIC);
+               ENUM_STR(ATTACHED_DEV_TYPE3_CHARGER_MUIC);
+               ENUM_STR(ATTACHED_DEV_NONE_TYPE3_MUIC);
+               ENUM_STR(ATTACHED_DEV_UNSUPPORTED_ID_MUIC);
+               ENUM_STR(ATTACHED_DEV_UNSUPPORTED_ID_VB_MUIC);
+               ENUM_STR(ATTACHED_DEV_TIMEOUT_OPEN_MUIC);
+               ENUM_STR(ATTACHED_DEV_WIRELESS_PAD_MUIC);
+               ENUM_STR(ATTACHED_DEV_POWERPACK_MUIC);
+               ENUM_STR(ATTACHED_DEV_UNDEFINED_RANGE_MUIC);
+               ENUM_STR(ATTACHED_DEV_WATER_MUIC);
+               ENUM_STR(ATTACHED_DEV_CHK_WATER_REQ);
+               ENUM_STR(ATTACHED_DEV_CHK_WATER_DRY_REQ);
+               ENUM_STR(ATTACHED_DEV_GAMEPAD_MUIC);
+               ENUM_STR(ATTACHED_DEV_CHECK_OCP);
+               ENUM_STR(ATTACHED_DEV_RDU_TA_MUIC);
+               ENUM_STR(ATTACHED_DEV_AFC_CHARGER_9V_DUPLI_MUIC);
+               ENUM_STR(ATTACHED_DEV_UNKNOWN_MUIC);
+               ENUM_STR(ATTACHED_DEV_NUM);
+       }
+       return "invalid";
+}
+
+bool muic_check_is_hv_dev(struct s2mu004_muic_data *muic_data)
+{
+       bool ret = false;
+       struct muic_platform_data *muic_pdata = muic_data->pdata;
+
+       switch (muic_pdata->attached_dev) {
+       case ATTACHED_DEV_AFC_CHARGER_PREPARE_MUIC:
+       case ATTACHED_DEV_AFC_CHARGER_PREPARE_DUPLI_MUIC:
+       case ATTACHED_DEV_AFC_CHARGER_5V_MUIC:
+       case ATTACHED_DEV_AFC_CHARGER_5V_DUPLI_MUIC:
+       case ATTACHED_DEV_AFC_CHARGER_9V_MUIC:
+       case ATTACHED_DEV_AFC_CHARGER_ERR_V_MUIC:
+       case ATTACHED_DEV_AFC_CHARGER_ERR_V_DUPLI_MUIC:
+       case ATTACHED_DEV_HV_ID_ERR_UNDEFINED_MUIC:
+       case ATTACHED_DEV_HV_ID_ERR_UNSUPPORTED_MUIC:
+       case ATTACHED_DEV_HV_ID_ERR_SUPPORTED_MUIC:
+               ret = true;
+               break;
+       default:
+               ret = false;
+               break;
+       }
+
+       if (debug_en_checklist)
+               pr_info("%s attached_dev(%d)[%c]\n",
+                       __func__, muic_pdata->attached_dev, (ret ? 'T' : 'F'));
+
+       return ret;
+}
+
+muic_attached_dev_t hv_muic_check_id_err(struct s2mu004_muic_data *muic_data,
+       muic_attached_dev_t new_dev)
+{
+       muic_attached_dev_t after_new_dev = new_dev;
+       struct muic_platform_data *muic_pdata = muic_data->pdata;
+
+       if (!muic_check_is_hv_dev(muic_data))
+               goto out;
+
+       switch (new_dev) {
+       case ATTACHED_DEV_TA_MUIC:
+               pr_info("%s cannot change HV(%d)->TA(%d)!\n",
+                       __func__, muic_pdata->attached_dev, new_dev);
+               after_new_dev = muic_pdata->attached_dev;
+               break;
+       case ATTACHED_DEV_UNDEFINED_CHARGING_MUIC:
+               pr_info("%s Undefined\n", __func__);
+               after_new_dev = ATTACHED_DEV_HV_ID_ERR_UNDEFINED_MUIC;
+               break;
+       case ATTACHED_DEV_UNSUPPORTED_ID_VB_MUIC:
+               pr_info("%s Unsupported\n", __func__);
+               after_new_dev = ATTACHED_DEV_HV_ID_ERR_UNSUPPORTED_MUIC;
+               break;
+       default:
+               pr_info("%s Supported\n", __func__);
+               after_new_dev = ATTACHED_DEV_HV_ID_ERR_SUPPORTED_MUIC;
+               break;
+       }
+out:
+       return after_new_dev;
+}
+
+static int s2mu004_hv_muic_write_reg(struct i2c_client *i2c, u8 reg, u8 value)
+{
+       u8 before_val, after_val;
+       int ret;
+
+       s2mu004_read_reg(i2c, reg, &before_val);
+       ret = s2mu004_write_reg(i2c, reg, value);
+       s2mu004_read_reg(i2c, reg, &after_val);
+
+       pr_info("%s reg[0x%02x] = [0x%02x] + [0x%02x] -> [0x%02x]\n",
+               __func__, reg, before_val, value, after_val);
+       return ret;
+}
+
+int s2mu004_muic_hv_update_reg(struct i2c_client *i2c,
+       const u8 reg, const u8 val, const u8 mask, const bool debug_en)
+{
+       u8 before_val, new_val, after_val;
+       int ret = 0;
+
+       ret = s2mu004_read_reg(i2c, reg, &before_val);
+       if (ret)
+               pr_err("%s failed to read REG(0x%02x) [%d]\n",
+                       __func__, reg, ret);
+
+       new_val = (val & mask) | (before_val & (~mask));
+
+       if (before_val ^ new_val) {
+               ret = s2mu004_hv_muic_write_reg(i2c, reg, new_val);
+               if (ret)
+                       pr_err("%s failed to write REG(0x%02x) [%d]\n",
+                                       __func__, reg, ret);
+       } else if (debug_en) {
+               pr_info("%s REG(0x%02x): already [0x%02x], don't write reg\n",
+                               __func__, reg, before_val);
+               goto out;
+       }
+
+       if (debug_en) {
+               ret = s2mu004_read_reg(i2c, reg, &after_val);
+               if (ret < 0)
+                       pr_err("%s failed to read REG(0x%02x) [%d]\n",
+                                       __func__, reg, ret);
+
+               pr_info("%s REG(0x%02x): [0x%02x]+[0x%02x:0x%02x]=[0x%02x]\n",
+                               __func__, reg, before_val,
+                               val, mask, after_val);
+       }
+
+out:
+       return ret;
+}
+
+void s2mu004_hv_muic_reset_hvcontrol_reg(struct s2mu004_muic_data *muic_data)
+{
+       struct i2c_client *i2c = muic_data->i2c;
+
+       s2mu004_hv_muic_write_reg(i2c, 0x4b, 0x00);
+       s2mu004_hv_muic_write_reg(i2c, 0x49, 0x00);
+       s2mu004_hv_muic_write_reg(i2c, 0x4a, 0x00);
+       s2mu004_hv_muic_write_reg(i2c, 0x5f, 0x01);
+
+       muic_data->is_afc_muic_prepare = false;
+       s2mu004_muic_set_afc_ready(muic_data, false);
+
+       cancel_delayed_work(&muic_data->afc_send_mpnack);
+       cancel_delayed_work(&muic_data->afc_control_ping_retry);
+       cancel_delayed_work(&muic_data->afc_qc_retry);
+}
+
+void s2mu004_muic_set_afc_ready(struct s2mu004_muic_data *muic_data, bool value)
+{
+       bool before, after;
+
+       before = muic_data->is_afc_muic_ready;
+       muic_data->is_afc_muic_ready = value;
+       after = muic_data->is_afc_muic_ready;
+
+       pr_info("%s afc_muic_ready[%d->%d]\n", __func__, before, after);
+}
+
+static int s2mu004_hv_muic_state_maintain(struct s2mu004_muic_data *muic_data)
+{
+       int ret = 0;
+       struct muic_platform_data *muic_pdata = muic_data->pdata;
+
+       pr_info("%s\n", __func__);
+
+       if (muic_pdata->attached_dev == ATTACHED_DEV_NONE_MUIC) {
+               pr_info("%s Detached(%d), need to check after\n",
+                               __func__, muic_pdata->attached_dev);
+               return ret;
+       }
+
+       return ret;
+}
+
+static void s2mu004_mpnack_irq_mask(struct s2mu004_muic_data *muic_data, int enable)
+{
+       pr_info("%s: irq enable: %d\n", __func__, enable);
+       if (enable)
+               s2mu004_muic_hv_update_reg(muic_data->i2c, 0x05, 0x00, 0x08, 0);
+       else
+               s2mu004_muic_hv_update_reg(muic_data->i2c, 0x05, 0x08, 0x08, 0);
+}
+
+static void s2mu004_hv_muic_set_afc_after_prepare
+                                       (struct s2mu004_muic_data *muic_data)
+{
+       pr_info("%s HV charger is detected\n", __func__);
+
+       muic_data->retry_cnt = 0;
+       s2mu004_mpnack_irq_mask(muic_data, 0);
+       schedule_delayed_work(&muic_data->afc_send_mpnack, msecs_to_jiffies(50));
+}
+
+void s2mu004_muic_afc_after_prepare(struct work_struct *work)
+{
+       struct s2mu004_muic_data *muic_data =
+               container_of(work, struct s2mu004_muic_data, afc_after_prepare.work);
+
+       pr_info("%s\n ", __func__);
+
+       mutex_lock(&muic_data->afc_mutex);
+
+       s2mu004_hv_muic_set_afc_after_prepare(muic_data);
+
+       mutex_unlock(&muic_data->afc_mutex);
+}
+
+#define RETRY_QC_CNT 0
+void s2mu004_muic_afc_qc_retry(struct work_struct *work)
+{
+       struct s2mu004_muic_data *muic_data =
+               container_of(work, struct s2mu004_muic_data, afc_qc_retry.work);
+
+       pr_info("%s retry_qc_cnt : (%d)\n ", __func__, muic_data->retry_qc_cnt);
+
+       if (muic_data->retry_qc_cnt < RETRY_QC_CNT) {
+               muic_data->retry_qc_cnt++;
+               s2mu004_hv_muic_write_reg(muic_data->i2c, 0x49, 0x00);
+               msleep(20);
+               s2mu004_hv_muic_write_reg(muic_data->i2c, 0x49, 0xbd);
+               schedule_delayed_work(&muic_data->afc_qc_retry, msecs_to_jiffies(100));
+       } else {
+               muic_data->retry_qc_cnt = 0;
+               muic_data->qc_prepare = 0;
+       }
+
+}
+
+#define RETRY_PING_CNT 0
+static void s2mu004_muic_afc_control_ping_retry(struct work_struct *work)
+{
+       struct s2mu004_muic_data *muic_data =
+               container_of(work, struct s2mu004_muic_data, afc_control_ping_retry.work);
+       pr_info("%s retry_cnt : %d\n", __func__, muic_data->retry_cnt);
+
+       if (muic_data->retry_cnt <  RETRY_PING_CNT) {
+               muic_data->retry_cnt++;
+               s2mu004_hv_muic_write_reg(muic_data->i2c, 0x4A, 0x0e);
+               schedule_delayed_work(&muic_data->afc_control_ping_retry, msecs_to_jiffies(50));
+       } else {
+               muic_data->retry_cnt = 0;
+               s2mu004_mpnack_irq_mask(muic_data, 1); /* enable mpnack irq */
+               s2mu004_hv_muic_write_reg(muic_data->i2c, 0x4A, 0x0e);
+       }
+}
+
+static void s2mu004_hv_muic_afc_control_ping
+               (struct s2mu004_muic_data *muic_data, bool ping_continue)
+{
+       pr_info("%s [%d, %c]\n", __func__,
+               muic_data->afc_count, ping_continue ? 'T' : 'F');
+       if (ping_continue) {
+               msleep(30);
+               s2mu004_hv_muic_write_reg(muic_data->i2c, 0x4A, 0x0e);
+               schedule_delayed_work(&muic_data->afc_send_mpnack, msecs_to_jiffies(2000));
+               schedule_delayed_work(&muic_data->afc_control_ping_retry, msecs_to_jiffies(50));
+       }
+}
+
+static int s2mu004_hv_muic_handle_attach
+               (struct s2mu004_muic_data *muic_data, const muic_afc_data_t *new_afc_data)
+{
+       int ret = 0;
+       bool noti = true;
+       muic_attached_dev_t new_dev = new_afc_data->new_dev;
+       struct muic_platform_data *muic_pdata = muic_data->pdata;
+
+       pr_info("%s %d\n", __func__, new_afc_data->function_num);
+
+       if (muic_data->is_charger_ready == false) {
+               if (new_afc_data->new_dev == ATTACHED_DEV_AFC_CHARGER_PREPARE_MUIC) {
+                       muic_data->is_afc_muic_prepare = true;
+                       pr_info("%s is_charger_ready[%c], is_afc_muic_prepare[%c]\n",
+                               __func__,
+                               (muic_data->is_charger_ready ? 'T' : 'F'),
+                               (muic_data->is_afc_muic_prepare ? 'T' : 'F'));
+
+                       return ret;
+               }
+               pr_info("%s is_charger_ready[%c], just return\n",
+                       __func__, (muic_data->is_charger_ready ? 'T' : 'F'));
+               return ret;
+       }
+
+       switch (new_afc_data->function_num) {
+       case FUNC_TA_TO_PREPARE:
+               schedule_delayed_work(&muic_data->afc_after_prepare, msecs_to_jiffies(60));
+               muic_data->afc_count = 0;
+               break;
+       case FUNC_PREPARE_TO_PREPARE_DUPLI:
+               muic_data->afc_count++;
+               if (muic_data->afc_count < 3) {
+                       s2mu004_hv_muic_afc_control_ping(muic_data, true);
+                       noti = false;
+               }
+               break;
+       case FUNC_PREPARE_TO_AFC_5V:
+               if (muic_data->afc_count > AFC_CHARGER_WA_PING) {
+                       s2mu004_hv_muic_afc_control_ping(muic_data, false);
+               } else {
+                       s2mu004_hv_muic_afc_control_ping(muic_data, true);
+                       noti = false;
+               }
+               break;
+       case FUNC_PREPARE_TO_QC_PREPARE:
+               muic_data->afc_count = 0;
+               muic_data->qc_prepare = 1;
+               msleep(60);
+               s2mu004_hv_muic_write_reg(muic_data->i2c, 0x5f, 0x01);
+               s2mu004_hv_muic_write_reg(muic_data->i2c, 0x49, 0xbd);
+               break;
+       case FUNC_PREPARE_DUPLI_TO_PREPARE_DUPLI:
+               pr_err("[DEBUG] %s(%d)dupli, dupli\n", __func__, __LINE__);
+               muic_data->afc_count++;
+               if (muic_data->afc_count < 3) {
+                       s2mu004_hv_muic_afc_control_ping(muic_data, true);
+                       noti = false;
+               }
+               break;
+       case FUNC_PREPARE_DUPLI_TO_AFC_5V:
+               break;
+       case FUNC_PREPARE_DUPLI_TO_AFC_ERR_V:
+               if (muic_data->afc_count > AFC_CHARGER_WA_PING) {
+                       s2mu004_hv_muic_afc_control_ping(muic_data, false);
+               } else {
+                       s2mu004_hv_muic_afc_control_ping(muic_data, true);
+                       noti = false;
+               }
+               break;
+       case FUNC_PREPARE_DUPLI_TO_AFC_9V:
+               s2mu004_hv_muic_afc_control_ping(muic_data, false);
+               break;
+       case FUNC_AFC_5V_TO_AFC_5V_DUPLI:
+               /*
+                * attached_dev is changed. MPING Missing did not happened
+                * Cancel delayed work
+                */
+               pr_info("%s cancel_delayed_work(dev %d), Mping missing wa\n",
+                       __func__, new_dev);
+               muic_data->afc_count++;
+               if (muic_data->afc_count < 3) {
+                       s2mu004_hv_muic_afc_control_ping(muic_data, true);
+                       noti = false;
+               }
+               break;
+       case FUNC_AFC_5V_TO_AFC_ERR_V:
+               /*
+                * attached_dev is changed. MPING Missing did not happened
+                * Cancel delayed work
+                */
+               pr_info("%s cancel_delayed_work(dev %d), Mping missing wa\n",
+                       __func__, new_dev);
+               if (muic_data->afc_count > AFC_CHARGER_WA_PING) {
+                       s2mu004_hv_muic_afc_control_ping(muic_data, false);
+               } else {
+                       s2mu004_hv_muic_afc_control_ping(muic_data, true);
+                       noti = false;
+               }
+               break;
+       case FUNC_AFC_5V_TO_AFC_9V:
+               /*
+                * attached_dev is changed. MPING Missing did not happened
+                * Cancel delayed work
+                */
+               pr_info("%s cancel_delayed_work(dev %d), Mping missing wa\n",
+                       __func__, new_dev);
+               s2mu004_hv_muic_write_reg(muic_data->i2c, 0x05, 0x00);
+
+               break;
+       case FUNC_AFC_5V_DUPLI_TO_AFC_5V_DUPLI:
+               muic_data->afc_count++;
+               if (muic_data->afc_count > AFC_CHARGER_WA_PING) {
+                       s2mu004_hv_muic_afc_control_ping(muic_data, false);
+               } else {
+                       s2mu004_hv_muic_afc_control_ping(muic_data, true);
+                       noti = false;
+               }
+               break;
+       case FUNC_AFC_5V_DUPLI_TO_AFC_ERR_V:
+               if (muic_data->afc_count > AFC_CHARGER_WA_PING) {
+                       s2mu004_hv_muic_afc_control_ping(muic_data, false);
+               } else {
+                       s2mu004_hv_muic_afc_control_ping(muic_data, true);
+                       noti = false;
+               }
+               break;
+       case FUNC_AFC_5V_DUPLI_TO_AFC_9V:
+               s2mu004_hv_muic_afc_control_ping(muic_data, false);
+#ifdef CONFIG_MUIC_HV_FORCE_LIMIT
+                       if (muic_data->pdata->silent_chg_change_state == SILENT_CHG_CHANGING)
+                               noti = false;
+#endif
+               break;
+       case FUNC_AFC_ERR_V_TO_AFC_ERR_V_DUPLI:
+               muic_data->afc_count++;
+               if (muic_data->afc_count > AFC_CHARGER_WA_PING) {
+                       s2mu004_hv_muic_afc_control_ping(muic_data, false);
+               } else {
+                       s2mu004_hv_muic_afc_control_ping(muic_data, true);
+                       noti = false;
+               }
+               break;
+       case FUNC_AFC_ERR_V_TO_AFC_9V:
+               s2mu004_hv_muic_afc_control_ping(muic_data, false);
+               break;
+       case FUNC_AFC_ERR_V_DUPLI_TO_AFC_ERR_V_DUPLI:
+               muic_data->afc_count++;
+               if (muic_data->afc_count > AFC_CHARGER_WA_PING) {
+                       s2mu004_hv_muic_afc_control_ping(muic_data, false);
+               } else {
+                       s2mu004_hv_muic_afc_control_ping(muic_data, true);
+                       noti = false;
+               }
+               break;
+       case FUNC_AFC_ERR_V_DUPLI_TO_AFC_9V:
+               s2mu004_hv_muic_afc_control_ping(muic_data, false);
+               break;
+       case FUNC_AFC_9V_TO_AFC_5V:
+               break;
+       case FUNC_AFC_9V_TO_AFC_9V:
+#ifdef CONFIG_MUIC_HV_FORCE_LIMIT
+               muic_data->afc_count++;
+               if (muic_data->afc_count > AFC_CHARGER_WA_PING)
+                       s2mu004_hv_muic_afc_control_ping(muic_data, false);
+               else
+                       pr_info("dummy int called [%d]\n", muic_data->afc_count);
+#else
+               muic_data->afc_count++;
+               if (muic_data->afc_count < AFC_CHARGER_WA_PING)
+                       s2mu004_hv_muic_afc_control_ping(muic_data, true);
+#endif
+               cancel_delayed_work(&muic_data->afc_control_ping_retry);
+               pr_info("cancel retry ping to prevent charger reset\n");
+               break;
+       case FUNC_QC_PREPARE_TO_QC_9V:
+               pr_info("%s FUNC_QC_PREPARE_TO_QC_9V\n", __func__);
+               cancel_delayed_work(&muic_data->afc_qc_retry);
+               break;
+       default:
+               pr_warn("%s undefinded hv function num(%d)\n",
+                                       __func__, new_afc_data->function_num);
+               ret = -ESRCH;
+               goto out;
+       }
+
+       if (muic_pdata->attached_dev == new_dev)
+               noti = false;
+       else if (new_dev == ATTACHED_DEV_AFC_CHARGER_PREPARE_DUPLI_MUIC ||
+               new_dev == ATTACHED_DEV_AFC_CHARGER_5V_DUPLI_MUIC ||
+               new_dev == ATTACHED_DEV_AFC_CHARGER_ERR_V_DUPLI_MUIC)
+               noti = false;
+
+       if (noti) {
+               if (new_dev == ATTACHED_DEV_AFC_CHARGER_5V_MUIC) {
+                       pr_err("%s: AFC CHARGER 5V MUIC delay cable noti\n", __func__);
+                       schedule_delayed_work(&muic_data->afc_cable_type_work, msecs_to_jiffies(80));
+               } else {
+                       MUIC_SEND_NOTI_ATTACH_ALL(new_dev);
+               }
+       }
+
+       muic_pdata->attached_dev = new_dev;
+out:
+       return ret;
+}
+
+static bool muic_check_hv_irq
+                       (struct s2mu004_muic_data *muic_data,
+                       const muic_afc_data_t *afc_data, int irq)
+{
+       int afc_irq = 0;
+       bool ret = false;
+
+       pr_info("%s irq %d , irq_dnres %d, irq_mrxrdy %d, irq_vbadc %d irq_mpnack %d irq_vdnmon %d\n",
+               __func__, irq, muic_data->irq_dnres, muic_data->irq_mrxrdy, muic_data->irq_vbadc,
+               muic_data->irq_mpnack, muic_data->irq_vdnmon);
+       /* change irq num to muic_afc_irq_t */
+
+       if (irq == muic_data->irq_vbadc)
+               afc_irq = MUIC_AFC_IRQ_VBADC;
+       else if (irq == muic_data->irq_mrxrdy)
+               afc_irq = MUIC_AFC_IRQ_MRXRDY;
+       else if (irq == muic_data->irq_vdnmon)
+               afc_irq = MUIC_AFC_IRQ_VDNMON;
+       else if (irq == muic_data->irq_mpnack)
+               afc_irq = MUIC_AFC_IRQ_MPNACK;
+       else {
+               pr_err("%s cannot find irq #(%d)\n", __func__, irq);
+               ret = false;
+               goto out;
+       }
+
+       pr_info("%s afc_irq %d , %d\n",
+               __func__, afc_data->afc_irq, afc_irq);
+       if (afc_data->afc_irq == afc_irq) {
+               ret = true;
+               goto out;
+       }
+
+       if (afc_data->afc_irq == MUIC_AFC_IRQ_DONTCARE) {
+               ret = true;
+               goto out;
+       }
+
+out:
+       if (debug_en_checklist)
+               pr_info("%s check_data dev(%d) irq(%d:%d) ret(%c)\n",
+                               __func__, afc_data->new_dev,
+                               afc_data->afc_irq, afc_irq, ret ? 'T' : 'F');
+       return ret;
+}
+
+static bool muic_check_status_vbadc
+                       (const muic_afc_data_t *afc_data, u8 vbadc)
+{
+       bool ret = false;
+
+       if (afc_data->status_vbadc == vbadc) {
+               ret = true;
+               goto out;
+       }
+
+       if (afc_data->status_vbadc == VBADC_AFC_5V) {
+               switch (vbadc) {
+               case VBADC_5_3V:
+               case VBADC_5_7V_6_3V:
+                       ret = true;
+                       goto out;
+               default:
+                       break;
+               }
+       }
+
+       if (afc_data->status_vbadc == VBADC_AFC_9V) {
+               switch (vbadc) {
+               case VBADC_8_7V_9_3V:
+               case VBADC_9_7V_10_3V:
+                       ret = true;
+                       goto out;
+               default:
+                       break;
+               }
+       }
+
+       if (afc_data->status_vbadc == VBADC_AFC_ERR_V_NOT_0) {
+               switch (vbadc) {
+               case VBADC_7_7V_8_3V:
+               case VBADC_10_7V_11_3V:
+               case VBADC_11_7V_12_3V:
+               case VBADC_12_7V_13_3V:
+               case VBADC_13_7V_14_3V:
+               case VBADC_14_7V_15_3V:
+               case VBADC_15_7V_16_3V:
+               case VBADC_16_7V_17_3V:
+               case VBADC_17_7V_18_3V:
+               case VBADC_18_7V_19_3V:
+                       ret = true;
+                       goto out;
+               default:
+                       break;
+               }
+       }
+
+       if (afc_data->status_vbadc == VBADC_AFC_ERR_V) {
+               switch (vbadc) {
+               case VBADC_7_7V_8_3V:
+               case VBADC_10_7V_11_3V:
+               case VBADC_11_7V_12_3V:
+               case VBADC_12_7V_13_3V:
+               case VBADC_13_7V_14_3V:
+               case VBADC_14_7V_15_3V:
+               case VBADC_15_7V_16_3V:
+               case VBADC_16_7V_17_3V:
+               case VBADC_17_7V_18_3V:
+               case VBADC_18_7V_19_3V:
+               case VBADC_19_7V:
+                       ret = true;
+                       goto out;
+               default:
+                       break;
+               }
+       }
+#if 0
+       if (afc_data->status_vbadc == VBADC_QC_5V) {
+               switch (vbadc) {
+               case VBADC_4V_5V:
+               case VBADC_5V_6V:
+                       ret = true;
+                       goto out;
+               default:
+                       break;
+               }
+       }
+
+       if (afc_data->status_vbadc == VBADC_QC_9V) {
+               switch (vbadc) {
+               case VBADC_6V_7V:
+               case VBADC_7V_8V:
+               case VBADC_8V_9V:
+               case VBADC_9V_10V:
+                       ret = true;
+                       goto out;
+               default:
+                       break;
+               }
+       }
+#endif
+       if (afc_data->status_vbadc == VBADC_ANY) {
+               switch (vbadc) {
+               case VBADC_5_3V:
+               case VBADC_5_7V_6_3V:
+               case VBADC_6_7V_7_3V:
+               case VBADC_7_7V_8_3V:
+               case VBADC_8_7V_9_3V:
+               case VBADC_9_7V_10_3V:
+               case VBADC_10_7V_11_3V:
+               case VBADC_11_7V_12_3V:
+               case VBADC_12_7V_13_3V:
+               case VBADC_13_7V_14_3V:
+               case VBADC_14_7V_15_3V:
+               case VBADC_15_7V_16_3V:
+               case VBADC_16_7V_17_3V:
+               case VBADC_17_7V_18_3V:
+               case VBADC_18_7V_19_3V:
+               case VBADC_19_7V:
+                       ret = true;
+                       goto out;
+               default:
+                       break;
+               }
+       }
+
+       if (afc_data->status_vbadc == VBADC_DONTCARE) {
+               ret = true;
+               goto out;
+       }
+
+out:
+       if (debug_en_checklist)
+               pr_info("%s check_data dev(%d) vbadc(0x%x:0x%x) ret(%c)\n",
+                               __func__, afc_data->new_dev,
+                               afc_data->status_vbadc, vbadc, ret ? 'T' : 'F');
+       return ret;
+}
+
+static bool muic_check_status_vdnmon
+                       (const muic_afc_data_t *afc_data, u8 vdnmon)
+{
+       bool ret = false;
+
+       if (afc_data->status_vdnmon == vdnmon) {
+               ret = true;
+               goto out;
+       }
+
+       if (afc_data->status_vdnmon == VDNMON_DONTCARE) {
+               ret = true;
+               goto out;
+       }
+
+out:
+       if (debug_en_checklist)
+               pr_info("%s check_data dev(%d) vdnmon(0x%x:0x%x) ret(%c)\n",
+                               __func__, afc_data->new_dev,
+                               afc_data->status_vdnmon, vdnmon, ret ? 'T' : 'F');
+
+       return ret;
+}
+
+bool muic_check_dev_ta(struct s2mu004_muic_data *muic_data)
+{
+       u8 status1 = muic_data->status1;
+       u8 status2 = muic_data->status2;
+       u8 status4 = muic_data->status4;
+       u8 adc, vbvolt, chgtyp;
+       struct muic_platform_data *muic_pdata = muic_data->pdata;
+
+       adc = status1 & ADC_MASK;
+       vbvolt = status2 & DEV_TYPE_APPLE_VBUS_WAKEUP;
+       chgtyp = status4 & DEV_TYPE_DCPCHG;
+
+       pr_info("%s adc %d\n", __func__, adc);
+       if (adc != ADC_OPEN) {
+               s2mu004_muic_set_afc_ready(muic_data, false);
+               return false;
+       }
+       if (vbvolt == 0 || chgtyp == 0) {
+               s2mu004_muic_set_afc_ready(muic_data, false);
+#if defined(CONFIG_MUIC_NOTIFIER)
+               muic_notifier_detach_attached_dev(muic_pdata->attached_dev);
+#endif
+               muic_pdata->attached_dev = ATTACHED_DEV_NONE_MUIC;
+               s2mu004_hv_muic_reset_hvcontrol_reg(muic_data);
+               return false;
+       }
+
+       return true;
+}
+
+static void s2mu004_hv_muic_detect_dev(struct s2mu004_muic_data *muic_data, int irq)
+{
+       struct i2c_client *i2c = muic_data->i2c;
+       struct muic_platform_data *muic_pdata = muic_data->pdata;
+       const muic_afc_data_t *afc_data = afc_condition_checklist[muic_pdata->attached_dev];
+#ifdef CONFIG_MUIC_MANAGER
+       struct muic_interface_t *muic_if = (struct muic_interface_t *)muic_data->if_data;
+#endif
+       int intr = MUIC_INTR_DETACH;
+       int ret;
+       int i;
+       u8 adc, dev_app, status, chgt;
+       u8 hvcontrol[2];
+       u8 vdnmon, vbadc;
+       bool flag_next = true;
+       bool muic_dev_ta = false;
+
+       if (afc_data == NULL) {
+               pr_err("%s non AFC Charger, just return!\n", __func__);
+               return;
+       }
+
+       ret = s2mu004_read_reg(i2c, S2MU004_REG_MUIC_ADC, &adc);
+       if (ret) {
+               pr_err("%s fail to read muic reg(%d)\n", __func__, ret);
+               return;
+       }
+
+       ret = s2mu004_read_reg(i2c, S2MU004_REG_MUIC_DEVICE_APPLE, &dev_app);
+       if (ret) {
+               pr_err("%s fail to read muic reg(%d)\n", __func__, ret);
+               return;
+       }
+
+       ret = s2mu004_read_reg(i2c, S2MU004_REG_AFC_STATUS, &status);
+       if (ret) {
+               pr_err("%s fail to read muic reg(%d)\n", __func__, ret);
+               return;
+       }
+
+       ret = s2mu004_read_reg(i2c, S2MU004_REG_MUIC_CHG_TYPE, &chgt);
+       if (ret) {
+               pr_err("%s fail to read muic reg(%d)\n", __func__, ret);
+               return;
+       }
+
+       pr_info("STATUS1:0x%02x, 2:0x%02x, 3:0x%02x\n",
+                       adc, dev_app, status);
+
+       muic_data->status1 = adc;
+       muic_data->status2 = dev_app;
+       muic_data->status3 = status;
+       muic_data->status4 = chgt;
+
+       /* check TA type */
+       muic_dev_ta = muic_check_dev_ta(muic_data);
+       if (!muic_dev_ta) {
+               pr_err("%s device type is not TA!\n", __func__);
+               return;
+       }
+
+       vdnmon = status & STATUS_VDNMON_MASK;
+       vbadc = status & STATUS_VBADC_MASK;
+
+       ret = s2mu004_bulk_read(i2c, S2MU004_REG_AFC_CTRL1, 2, hvcontrol);
+       if (ret) {
+               pr_err("%s fail to read muic reg(%d)\n",
+                               __func__, ret);
+               return;
+       }
+
+       pr_info("%s HVCONTROL1:0x%02x, 2:0x%02x\n", __func__,
+               hvcontrol[0], hvcontrol[1]);
+
+       /* attached - control */
+       muic_data->hvcontrol1 = hvcontrol[0];
+       muic_data->hvcontrol2 = hvcontrol[1];
+       pr_info("%s vbadc:0x%x\n", __func__, vbadc);
+
+       for (i = 0; i < 10; i++, afc_data = afc_data->next) {
+
+               if (!flag_next) {
+                       pr_info("%s not found new_dev in afc_condition_checklist\n",
+                               __func__);
+                       break;
+               }
+
+               pr_err("%s afc_data->name %s\n",
+                       __func__, afc_data->afc_name);
+               if (afc_data->next == afc_data)
+                       flag_next = false;
+
+               if (!(muic_check_hv_irq(muic_data, afc_data, irq)))
+                       continue;
+
+               if (!(muic_check_status_vbadc(afc_data, vbadc)))
+                       continue;
+
+               if (!(muic_check_status_vdnmon(afc_data, vdnmon)))
+                       continue;
+
+               pr_err("%s checklist match found at i(%d), %s(%d)\n",
+                       __func__, i, afc_data->afc_name,
+                       afc_data->new_dev);
+
+               intr = MUIC_INTR_ATTACH;
+
+               break;
+       }
+
+       if (intr == MUIC_INTR_ATTACH) {
+               pr_err("%s AFC ATTACHED %d->%d\n", __func__,
+                       muic_pdata->attached_dev, afc_data->new_dev);
+#ifdef CONFIG_MUIC_MANAGER
+               muic_manager_set_legacy_dev(muic_if, afc_data->new_dev);
+#endif
+               ret = s2mu004_hv_muic_handle_attach(muic_data, afc_data);
+               if (ret)
+                       pr_err("%s cannot handle attach(%d)\n",
+                               __func__, ret);
+       } else {
+               pr_info("%s AFC MAINTAIN (%d)\n", __func__,
+                               muic_pdata->attached_dev);
+               ret = s2mu004_hv_muic_state_maintain(muic_data);
+               if (ret)
+                       pr_err("%s cannot maintain state(%d)\n",
+                               __func__, ret);
+               goto out;
+       }
+
+out:
+       return;
+}
+
+#define MASK(width, shift)     (((0x1 << (width)) - 1) << shift)
+#define CHGIN_STATUS_SHIFT     5
+#define CHGIN_STATUS_WIDTH     3
+#define CHGIN_STATUS_MASK      MASK(CHGIN_STATUS_WIDTH, CHGIN_STATUS_SHIFT)
+
+void s2mu004_muic_afc_check_vbadc(struct work_struct *work)
+{
+       struct s2mu004_muic_data *muic_data =
+               container_of(work, struct s2mu004_muic_data, afc_check_vbadc.work);
+       int ret;
+       u8 val_vbadc, chg_sts0;
+       struct muic_platform_data *muic_pdata = muic_data->pdata;
+
+       ret = s2mu004_read_reg(muic_data->i2c, S2MU004_REG_AFC_STATUS, &val_vbadc);
+       if (ret)
+               pr_err("%s fail to read muic reg(%d)\n", __func__, ret);
+
+       ret = s2mu004_read_reg(muic_data->i2c, 0X0A, &chg_sts0);
+       if (ret)
+               pr_err("%s fail to read muic chg reg(%d)\n", __func__, ret);
+
+       chg_sts0 = chg_sts0 & CHGIN_STATUS_MASK;
+       val_vbadc = val_vbadc & STATUS_VBADC_MASK;
+       pr_err("%s(%d) vbadc : %d, attached_dev : %d, chg in: %02x\n", __func__, __LINE__,
+               val_vbadc, muic_pdata->attached_dev, chg_sts0);
+
+       if ((muic_pdata->attached_dev != ATTACHED_DEV_AFC_CHARGER_9V_MUIC)
+               && (!(chg_sts0 == 0x0))
+               && (val_vbadc == VBADC_8_7V_9_3V)) {
+               mutex_lock(&muic_data->muic_mutex);
+               s2mu004_hv_muic_detect_dev(muic_data, muic_data->irq_vbadc);
+               mutex_unlock(&muic_data->muic_mutex);
+       }
+
+}
+
+void s2mu004_muic_afc_cable_type_work(struct work_struct *work)
+{
+       struct s2mu004_muic_data *muic_data =
+               container_of(work, struct s2mu004_muic_data, afc_cable_type_work.work);
+       int ret;
+       u8 val_vbadc, chg_sts0;
+       struct muic_platform_data *muic_pdata = muic_data->pdata;
+
+       ret = s2mu004_read_reg(muic_data->i2c, S2MU004_REG_AFC_STATUS, &val_vbadc);
+       if (ret)
+               pr_err("%s fail to read muic reg(%d)\n", __func__, ret);
+
+       ret = s2mu004_read_reg(muic_data->i2c, 0X0A, &chg_sts0);
+       if (ret)
+               pr_err("%s fail to read muic chg reg(%d)\n", __func__, ret);
+
+       chg_sts0 = chg_sts0 & CHGIN_STATUS_MASK;
+       val_vbadc = val_vbadc & STATUS_VBADC_MASK;
+       pr_err("%s(%d) vbadc : %d, attached_dev : %d, chg in: %02x\n", __func__, __LINE__,
+               val_vbadc, muic_pdata->attached_dev, chg_sts0);
+
+       if (muic_pdata->attached_dev != ATTACHED_DEV_NONE_MUIC
+               && (val_vbadc != VBADC_8_7V_9_3V)
+               && (!(chg_sts0 == 0x0))) {
+               mutex_lock(&muic_data->muic_mutex);
+               MUIC_SEND_NOTI_ATTACH(ATTACHED_DEV_AFC_CHARGER_5V_MUIC);
+               mutex_unlock(&muic_data->muic_mutex);
+       }
+
+}
+
+void s2mu004_muic_afc_send_mpnack(struct work_struct *work)
+{
+       struct s2mu004_muic_data *muic_data =
+               container_of(work, struct s2mu004_muic_data, afc_send_mpnack.work);
+       pr_info("%s\n ", __func__);
+       mutex_lock(&muic_data->afc_mutex);
+       s2mu004_hv_muic_detect_dev(muic_data, muic_data->irq_mpnack);
+       mutex_unlock(&muic_data->afc_mutex);
+}
+
+/* TA setting in s2mu004-muic.c */
+void s2mu004_muic_prepare_afc_charger(struct s2mu004_muic_data *muic_data)
+{
+       struct i2c_client *i2c = muic_data->i2c;
+
+       pr_info("%s\n", __func__);
+       msleep(200);
+       s2mu004_write_reg(i2c, 0x49, 0xa0);
+
+       /* Set TX DATA */
+       muic_data->tx_data = (HVTXBYTE_9V << 4) | HVTXBYTE_1_65A;
+       s2mu004_write_reg(i2c, S2MU004_REG_TX_BYTE1, 0x46);
+       s2mu004_write_reg(i2c, 0x49, 0xa1);
+       s2mu004_write_reg(i2c, 0x4a, 0x06);
+       s2mu004_muic_set_afc_ready(muic_data, true);
+}
+
+/* TA setting in s2mu004-muic.c */
+bool s2mu004_muic_check_change_dev_afc_charger(struct s2mu004_muic_data *muic_data,
+       muic_attached_dev_t new_dev)
+{
+       bool ret = true;
+
+       if (new_dev == ATTACHED_DEV_TA_MUIC ||
+               new_dev == ATTACHED_DEV_AFC_CHARGER_PREPARE_MUIC ||
+               new_dev == ATTACHED_DEV_AFC_CHARGER_PREPARE_DUPLI_MUIC ||
+               new_dev == ATTACHED_DEV_AFC_CHARGER_5V_MUIC ||
+               new_dev == ATTACHED_DEV_AFC_CHARGER_5V_DUPLI_MUIC ||
+               new_dev == ATTACHED_DEV_AFC_CHARGER_9V_MUIC ||
+               new_dev == ATTACHED_DEV_AFC_CHARGER_ERR_V_MUIC ||
+               new_dev == ATTACHED_DEV_AFC_CHARGER_ERR_V_DUPLI_MUIC){
+               if (muic_check_dev_ta(muic_data))
+                       ret = false;
+       }
+
+       return ret;
+}
+
+static void s2mu004_hv_muic_detect_after_charger_init(struct work_struct *work)
+{
+       struct afc_init_data_s *init_data =
+           container_of(work, struct afc_init_data_s, muic_afc_init_work);
+       struct s2mu004_muic_data *muic_data = init_data->muic_data;
+       int ret;
+       u8 status3;
+
+       pr_info("%s\n", __func__);
+
+       mutex_lock(&muic_data->muic_mutex);
+
+       /* check vdnmon status value */
+       ret = s2mu004_read_reg(muic_data->i2c, S2MU004_REG_AFC_STATUS, &status3);
+       if (ret) {
+               pr_err("%s fail to read muic reg(%d)\n",
+                               __func__, ret);
+               return;
+       }
+       pr_info("%s STATUS3:0x%02x\n", __func__, status3);
+
+       if (muic_data->is_afc_muic_ready) {
+               if (muic_data->is_afc_muic_prepare)
+                       s2mu004_hv_muic_detect_dev(muic_data, -1);
+       }
+
+       mutex_unlock(&muic_data->muic_mutex);
+}
+
+#ifdef CONFIG_HV_MUIC_VOLTAGE_CTRL
+void hv_muic_change_afc_voltage(int tx_data)
+{
+       struct i2c_client *i2c = afc_init_data.muic_data->i2c;
+       struct s2mu004_muic_data *muic_data = afc_init_data.muic_data;
+       u8 value;
+       struct muic_platform_data *muic_pdata = muic_data->pdata;
+
+       pr_info("%s %x\n", __func__, tx_data);
+
+       /* QC */
+       if (muic_pdata->attached_dev == ATTACHED_DEV_QC_CHARGER_9V_MUIC) {
+               switch (tx_data) {
+               case MUIC_HV_5V:
+                       s2mu004_hv_muic_write_reg(i2c, 0x49, 0xa1);
+                       break;
+               case MUIC_HV_9V:
+                       s2mu004_hv_muic_write_reg(i2c, 0x49, 0xbd);
+                       break;
+               default:
+                       break;
+               }
+       } else { /* AFC */
+               muic_data->afc_count = 0;
+               s2mu004_read_reg(i2c, S2MU004_REG_TX_BYTE1, &value);
+               if (value == tx_data) {
+                       pr_info("%s: same to current voltage %x\n", __func__, value);
+                       return;
+               }
+
+               s2mu004_write_reg(i2c, S2MU004_REG_TX_BYTE1, tx_data);
+               s2mu004_hv_muic_afc_control_ping(muic_data, true);
+       }
+}
+
+int muic_afc_set_voltage(int vol)
+{
+       if (vol == 5) {
+               hv_muic_change_afc_voltage(MUIC_HV_5V);
+       } else if (vol == 9) {
+               hv_muic_change_afc_voltage(MUIC_HV_9V);
+       } else {
+               pr_warn("%s invalid value\n", __func__);
+               return 0;
+       }
+
+       return 1;
+}
+#endif /* CONFIG_HV_MUIC_VOLTAGE_CTRL */
+
+void s2mu004_hv_muic_charger_init(void)
+{
+       pr_info("%s\n", __func__);
+
+       if (afc_init_data.muic_data) {
+               afc_init_data.muic_data->is_charger_ready = true;
+               schedule_work(&afc_init_data.muic_afc_init_work);
+       }
+}
+
+void s2mu004_hv_muic_init_detect(struct s2mu004_muic_data *muic_data)
+{
+
+       pr_info("%s\n", __func__);
+       /* TBD */
+#if 0
+       mutex_lock(&muic_data->muic_mutex);
+
+       mutex_unlock(&muic_data->muic_mutex);
+#endif
+}
+static irqreturn_t s2mu004_muic_hv_irq(int irq, void *data)
+{
+       struct s2mu004_muic_data *muic_data = data;
+       struct muic_platform_data *muic_pdata = muic_data->pdata;
+       struct irq_desc *desc = irq_to_desc(irq);
+       int ret;
+       u8 val_vbadc;
+
+       pr_info("%s %s start\n", __func__, desc ? desc->action->name : "-1");
+
+       mutex_lock(&muic_data->muic_mutex);
+
+       if (muic_data->is_afc_muic_ready == false)
+               pr_info("%s not ready yet(afc_muic_ready[%c])\n",
+                        __func__, (muic_data->is_afc_muic_ready ? 'T' : 'F'));
+       else if (muic_data->is_charger_ready == false && irq != muic_data->irq_vdnmon)
+               pr_info("%s not ready yet(charger_ready[%c])\n",
+                       __func__, (muic_data->is_charger_ready ? 'T' : 'F'));
+       else if (muic_data->pdata->afc_disable)
+               pr_info("%s AFC disable by USER (afc_disable[%c]\n",
+                       __func__, (muic_data->pdata->afc_disable ? 'T' : 'F'));
+       else {
+               muic_data->afc_irq = irq;
+
+               /* Re-check vbadc voltage, if vbadc 9v interrupt does not occur when send pings. */
+               if (irq == muic_data->irq_vbadc && muic_data->afc_count >= 2) {
+                       pr_err("%s: VbADC interrupt after sending master ping x3\n", __func__);
+                       ret = s2mu004_read_reg(muic_data->i2c, S2MU004_REG_AFC_STATUS, &val_vbadc);
+                       if (ret)
+                               pr_err("%s fail to read muic reg(%d)\n", __func__, ret);
+
+                       val_vbadc = val_vbadc & STATUS_VBADC_MASK;
+                       if (val_vbadc != VBADC_8_7V_9_3V) {
+                               pr_err("%s: vbadc: %d\n", __func__, val_vbadc);
+                               schedule_delayed_work(&muic_data->afc_check_vbadc, msecs_to_jiffies(100));
+                       }
+               }
+               /* After ping , if there is response then cancle mpnack work */
+               if ((irq == muic_data->irq_mrxrdy) || (irq == muic_data->irq_mpnack)) {
+                       cancel_delayed_work(&muic_data->afc_send_mpnack);
+                       cancel_delayed_work(&muic_data->afc_control_ping_retry);
+               }
+
+               if ((irq == muic_data->irq_vbadc) && (muic_data->qc_prepare == 1)) {
+                       muic_data->qc_prepare = 0;
+                       cancel_delayed_work(&muic_data->afc_qc_retry);
+               }
+
+               s2mu004_hv_muic_detect_dev(muic_data, muic_data->afc_irq);
+
+       }
+
+       mutex_unlock(&muic_data->muic_mutex);
+
+       pr_info("%s done(%s)\n", __func__, dev_to_str(muic_pdata->attached_dev));
+
+       return IRQ_HANDLED;
+}
+
+#define REQUEST_HV_IRQ(_irq, _dev_id, _name)                           \
+do {                                                                   \
+       ret = request_threaded_irq(_irq, NULL, s2mu004_muic_hv_irq,     \
+                               IRQF_NO_SUSPEND, _name, _dev_id);       \
+       if (ret < 0) {                                                  \
+               pr_err("%s Failed to request IRQ #%d: %d\n",            \
+                               __func__, _irq, ret);   \
+               _irq = 0;                                               \
+       }                                                               \
+} while (0)
+
+int s2mu004_afc_muic_irq_init(struct s2mu004_muic_data *muic_data)
+{
+       int ret = 0;
+
+       pr_info("%s\n", __func__);
+
+       if (muic_data->mfd_pdata && (muic_data->mfd_pdata->irq_base > 0)) {
+               int irq_base = muic_data->mfd_pdata->irq_base;
+
+               /* request AFC MUIC IRQ */
+               muic_data->irq_vdnmon = irq_base + S2MU004_AFC_IRQ_VDNMon;
+               REQUEST_HV_IRQ(muic_data->irq_vdnmon, muic_data, "muic-vdnmon");
+               muic_data->irq_mrxrdy = irq_base + S2MU004_AFC_IRQ_MRxRdy;
+               REQUEST_HV_IRQ(muic_data->irq_mrxrdy, muic_data, "muic-mrxrdy");
+               muic_data->irq_vbadc = irq_base + S2MU004_AFC_IRQ_VbADC;
+               REQUEST_HV_IRQ(muic_data->irq_vbadc, muic_data, "muic-vbadc");
+
+               muic_data->irq_mpnack = irq_base + S2MU004_AFC_IRQ_MPNack;
+               REQUEST_HV_IRQ(muic_data->irq_mpnack, muic_data, "muic-mpnack");
+
+               pr_info("mrxrdy(%d), mpnack(%d), vbadc(%d), vdnmon(%d)\n",
+                               muic_data->irq_dnres, muic_data->irq_mrxrdy,
+                               muic_data->irq_vbadc, muic_data->irq_vdnmon);
+       }
+
+       return ret;
+}
+
+#define FREE_HV_IRQ(_irq, _dev_id, _name)      \
+do {                                                                   \
+       if (_irq) {                                                     \
+               free_irq(_irq, _dev_id);                                \
+               pr_info("%s IRQ(%d):%s free done\n",    \
+                               __func__, _irq, _name);                 \
+       }                                                               \
+} while (0)
+
+void s2mu004_hv_muic_free_irqs(struct s2mu004_muic_data *muic_data)
+{
+       pr_info("%s\n", __func__);
+
+       /* free MUIC IRQ */
+       FREE_HV_IRQ(muic_data->irq_vdnmon, muic_data, "muic-vdnmon");
+       FREE_HV_IRQ(muic_data->irq_mrxrdy, muic_data, "muic-mrxrdy");
+       FREE_HV_IRQ(muic_data->irq_mpnack, muic_data, "muic-mpnack");
+       FREE_HV_IRQ(muic_data->irq_vbadc, muic_data, "muic-vbadc");
+}
+
+void s2mu004_hv_muic_initialize(struct s2mu004_muic_data *muic_data)
+{
+       pr_info("%s\n", __func__);
+
+       muic_data->is_afc_handshaking = false;
+       muic_data->is_afc_muic_prepare = false;
+       muic_data->is_charger_ready = true;
+       muic_data->pdata->afc_disable = false;
+
+       s2mu004_write_reg(muic_data->i2c, 0xd8, 0x84); /* OTP */
+       s2mu004_write_reg(muic_data->i2c, 0x2c, 0x55); /* OTP */
+       s2mu004_write_reg(muic_data->i2c, 0xc3, 0x88); /* OTP */
+
+       s2mu004_hv_muic_write_reg(muic_data->i2c, 0x4b, 0x00);
+       s2mu004_hv_muic_write_reg(muic_data->i2c, 0x49, 0x00);
+       s2mu004_hv_muic_write_reg(muic_data->i2c, 0x4a, 0x00);
+       s2mu004_hv_muic_write_reg(muic_data->i2c, 0x5f, 0x01);
+
+       afc_init_data.muic_data = muic_data;
+       INIT_WORK(&afc_init_data.muic_afc_init_work, s2mu004_hv_muic_detect_after_charger_init);
+
+       INIT_DELAYED_WORK(&muic_data->afc_check_vbadc, s2mu004_muic_afc_check_vbadc);
+       INIT_DELAYED_WORK(&muic_data->afc_cable_type_work, s2mu004_muic_afc_cable_type_work);
+
+       INIT_DELAYED_WORK(&muic_data->afc_send_mpnack, s2mu004_muic_afc_send_mpnack);
+       INIT_DELAYED_WORK(&muic_data->afc_control_ping_retry, s2mu004_muic_afc_control_ping_retry);
+       INIT_DELAYED_WORK(&muic_data->afc_qc_retry, s2mu004_muic_afc_qc_retry);
+       INIT_DELAYED_WORK(&muic_data->afc_after_prepare, s2mu004_muic_afc_after_prepare);
+
+       mutex_init(&muic_data->afc_mutex);
+}
+
+void s2mu004_hv_muic_remove(struct s2mu004_muic_data *muic_data)
+{
+       pr_info("%s\n", __func__);
+
+       /* Set digital IVR when power off under revision EVT3 */
+       if (muic_data->ic_rev_id < 3)
+               s2mu004_muic_hv_update_reg(muic_data->i2c, 0xB3, 0x00, 0x08, 0);
+
+       cancel_work_sync(&afc_init_data.muic_afc_init_work);
+       s2mu004_hv_muic_free_irqs(muic_data);
+
+       /* Afc reset */
+       s2mu004_muic_hv_update_reg(muic_data->i2c, 0x5f, 0x80, 0x80, 0);
+}
+#if 0
+void s2mu004_hv_muic_remove_wo_free_irq(struct s2mu004_muic_data *muic_data)
+{
+       pr_info("%s\n", __func__);
+       cancel_work_sync(&afc_init_data.muic_afc_init_work);
+}
+#endif
diff --git a/drivers/muic/s2mu004-muic-notifier.c b/drivers/muic/s2mu004-muic-notifier.c
new file mode 100644 (file)
index 0000000..1344b59
--- /dev/null
@@ -0,0 +1,409 @@
+
+#define pr_fmt(fmt)    "[MUIC] " fmt
+
+#include <linux/device.h>
+#include <linux/notifier.h>
+#include <linux/switch.h>
+#include <linux/muic/muic_core.h>
+#include <linux/muic/s2mu004-muic-notifier.h>
+#include <linux/sec_sysfs.h>
+
+/*
+ * The src & dest addresses of the noti.
+ * keep the same value defined in ccic_notifier.h
+ *     b'0001 : CCIC
+ *     b'0010 : MUIC
+ *     b'1111 : Broadcasting
+ */
+#define NOTI_ADDR_SRC (1 << 1)
+#define NOTI_ADDR_DST (0xf)
+
+/* ATTACH Noti. ID */
+#define NOTI_ID_ATTACH (1)
+
+#define SET_MUIC_NOTIFIER_BLOCK(nb, fn, dev) do {      \
+               (nb)->notifier_call = (fn);             \
+               (nb)->priority = (dev);                 \
+       } while (0)
+
+#define DESTROY_MUIC_NOTIFIER_BLOCK(nb)                        \
+               SET_MUIC_NOTIFIER_BLOCK(nb, NULL, -1)
+
+static struct muic_notifier_struct muic_notifier;
+#if defined(CONFIG_CCIC_S2MU004)
+static struct muic_notifier_struct muic_ccic_notifier;
+#endif
+
+static int muic_uses_new_noti;
+#if defined(CONFIG_CCIC_S2MU004)
+static int muic_ccic_uses_new_noti;
+#endif
+
+#if IS_ENABLED(CONFIG_SEC_FACTORY)
+struct switch_dev switch_muic_dev = {
+       .name = "attached_muic_cable",
+};
+
+static void send_muic_cable_intent(int type)
+{
+       pr_info("%s: MUIC attached_muic_cable type(%d)\n", __func__, type);
+       switch_set_state(&switch_muic_dev, type);
+}
+#endif
+
+void muic_notifier_set_new_noti(bool flag)
+{
+       muic_uses_new_noti = flag ? 1 : 0;
+}
+
+static void __set_noti_cxt(int attach, int type)
+{
+       if (type < 0) {
+               muic_notifier.cmd = attach;
+#if defined(CONFIG_MUIC_MANAGER) && defined(CONFIG_CCIC_NOTIFIER)
+               muic_notifier.cxt.attach = attach;
+#endif
+               return;
+       }
+
+       /* Old Interface */
+       muic_notifier.cmd = attach;
+       muic_notifier.attached_dev = type;
+
+#if defined(CONFIG_MUIC_MANAGER) && defined(CONFIG_CCIC_NOTIFIER)
+       /* New Interface */
+       muic_notifier.cxt.src = NOTI_ADDR_SRC;
+       muic_notifier.cxt.dest = NOTI_ADDR_DST;
+       muic_notifier.cxt.id = NOTI_ID_ATTACH;
+       muic_notifier.cxt.attach = attach;
+       muic_notifier.cxt.cable_type = type;
+       muic_notifier.cxt.rprd = 0;
+#endif
+}
+
+#if defined(CONFIG_CCIC_S2MU004)
+static void __set_ccic_noti_cxt(int attach, int type)
+{
+       if (type < 0) {
+               muic_ccic_notifier.cmd = attach;
+#if defined(CONFIG_MUIC_MANAGER) && defined(CONFIG_CCIC_NOTIFIER)
+               muic_ccic_notifier.cxt.attach = attach;
+#endif
+               return;
+       }
+
+       /* Old Interface */
+       muic_ccic_notifier.cmd = attach;
+       muic_ccic_notifier.attached_dev = type;
+
+#if defined(CONFIG_MUIC_MANAGER) && defined(CONFIG_CCIC_NOTIFIER)
+       /* New Interface */
+       muic_ccic_notifier.cxt.src = NOTI_ADDR_SRC;
+       muic_ccic_notifier.cxt.dest = NOTI_ADDR_DST;
+       muic_ccic_notifier.cxt.id = NOTI_ID_ATTACH;
+       muic_ccic_notifier.cxt.attach = attach;
+       muic_ccic_notifier.cxt.cable_type = type;
+       muic_ccic_notifier.cxt.rprd = 0;
+#endif
+}
+#endif
+
+int muic_notifier_register(struct notifier_block *nb, notifier_fn_t notifier,
+                       muic_notifier_device_t listener)
+{
+       int ret = 0;
+#if defined(CONFIG_MUIC_MANAGER) && defined(CONFIG_CCIC_NOTIFIER)
+       void *pcxt;
+#endif
+
+       pr_info("%s: listener=%d register\n", __func__, listener);
+
+       SET_MUIC_NOTIFIER_BLOCK(nb, notifier, listener);
+       ret = blocking_notifier_chain_register(&(muic_notifier.notifier_call_chain), nb);
+       if (ret < 0)
+               pr_err("%s: blocking_notifier_chain_register error(%d)\n",
+                               __func__, ret);
+
+#if defined(CONFIG_MUIC_MANAGER) && defined(CONFIG_CCIC_NOTIFIER)
+       pcxt = muic_uses_new_noti ? &(muic_notifier.cxt) :
+                       (void *)&(muic_notifier.attached_dev);
+
+       /* current muic's attached_device status notify */
+       nb->notifier_call(nb, muic_notifier.cxt.attach, pcxt);
+#else
+       nb->notifier_call(nb, muic_notifier.cmd,
+                       &(muic_notifier.attached_dev));
+#endif
+
+       return ret;
+}
+
+int muic_notifier_unregister(struct notifier_block *nb)
+{
+       int ret = 0;
+
+       pr_info("%s: listener=%d unregister\n", __func__, nb->priority);
+
+       ret = blocking_notifier_chain_unregister(&(muic_notifier.notifier_call_chain), nb);
+       if (ret < 0)
+               pr_err("%s: blocking_notifier_chain_unregister error(%d)\n",
+                               __func__, ret);
+       DESTROY_MUIC_NOTIFIER_BLOCK(nb);
+
+       return ret;
+}
+
+static int muic_notifier_notify(void)
+{
+       int ret = 0;
+#if defined(CONFIG_MUIC_MANAGER) && defined(CONFIG_CCIC_NOTIFIER)
+       void *pcxt;
+
+       pr_info("%s: CMD=%d, DATA=%d\n", __func__, muic_notifier.cxt.attach,
+                       muic_notifier.cxt.cable_type);
+
+       pcxt = muic_uses_new_noti ? &(muic_notifier.cxt) :
+                       (void *)&(muic_notifier.attached_dev);
+
+       ret = blocking_notifier_call_chain(&(muic_notifier.notifier_call_chain),
+                       muic_notifier.cxt.attach, pcxt);
+#else
+       pr_info("%s: CMD=%d, DATA=%d\n", __func__, muic_notifier.cmd,
+                       muic_notifier.attached_dev);
+       ret = blocking_notifier_call_chain(&(muic_notifier.notifier_call_chain),
+                       muic_notifier.cmd, &(muic_notifier.attached_dev));
+#endif
+
+#if IS_ENABLED(CONFIG_SEC_FACTORY)
+#if defined(CONFIG_MUIC_SUPPORT_CCIC) && defined(CONFIG_CCIC_NOTIFIER)
+       if (muic_notifier.cxt.attach != 0)
+               send_muic_cable_intent(muic_notifier.cxt.cable_type);
+       else
+               send_muic_cable_intent(0);
+#else
+       send_muic_cable_intent(muic_notifier.attached_dev);
+#endif /* CONFIG_MUIC_SUPPORT_CCIC */
+#endif /* CONFIG_SEC_FACTORY */
+
+       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;
+}
+
+#if defined(CONFIG_CCIC_S2MU004)
+int muic_ccic_notifier_register(struct notifier_block *nb, notifier_fn_t notifier,
+                       muic_notifier_device_t listener)
+{
+       int ret = 0;
+#if defined(CONFIG_MUIC_MANAGER) && defined(CONFIG_CCIC_NOTIFIER)
+       void *pcxt;
+#endif
+
+       pr_info("%s: listener=%d register\n", __func__, listener);
+
+       SET_MUIC_NOTIFIER_BLOCK(nb, notifier, listener);
+       ret = blocking_notifier_chain_register(&(muic_ccic_notifier.notifier_call_chain), nb);
+       if (ret < 0)
+               pr_err("%s: blocking_notifier_chain_register error(%d)\n",
+                               __func__, ret);
+
+#if defined(CONFIG_MUIC_MANAGER) && defined(CONFIG_CCIC_NOTIFIER)
+       pcxt = muic_ccic_uses_new_noti ? &(muic_ccic_notifier.cxt) :
+                       (void *)&(muic_ccic_notifier.attached_dev);
+
+       /* current muic's attached_device status notify */
+       nb->notifier_call(nb, muic_ccic_notifier.cxt.attach, pcxt);
+#else
+       nb->notifier_call(nb, muic_ccic_notifier.cmd,
+                       &(muic_ccic_notifier.attached_dev));
+#endif
+
+       return ret;
+}
+
+int muic_ccic_notifier_unregister(struct notifier_block *nb)
+{
+       int ret = 0;
+
+       pr_info("%s: listener=%d unregister\n", __func__, nb->priority);
+
+       ret = blocking_notifier_chain_unregister(&(muic_ccic_notifier.notifier_call_chain), nb);
+       if (ret < 0)
+               pr_err("%s: blocking_notifier_chain_unregister error(%d)\n",
+                               __func__, ret);
+       DESTROY_MUIC_NOTIFIER_BLOCK(nb);
+
+       return ret;
+}
+
+static int muic_ccic_notifier_notify(void)
+{
+       int ret = 0;
+#if defined(CONFIG_MUIC_MANAGER) && defined(CONFIG_CCIC_NOTIFIER)
+       void *pcxt;
+
+       pr_info("%s: CMD=%d, DATA=%d\n", __func__, muic_ccic_notifier.cxt.attach,
+                       muic_ccic_notifier.cxt.cable_type);
+
+       pcxt = muic_ccic_uses_new_noti ? &(muic_ccic_notifier.cxt) :
+                       (void *)&(muic_ccic_notifier.attached_dev);
+
+       ret = blocking_notifier_call_chain(&(muic_ccic_notifier.notifier_call_chain),
+                       muic_ccic_notifier.cxt.attach, pcxt);
+#else
+       pr_info("%s: CMD=%d, DATA=%d\n", __func__, muic_ccic_notifier.cmd,
+                       muic_ccic_notifier.attached_dev);
+       ret = blocking_notifier_call_chain(&(muic_ccic_notifier.notifier_call_chain),
+                       muic_ccic_notifier.cmd, &(muic_ccic_notifier.attached_dev));
+#endif
+
+       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;
+}
+#endif
+
+void muic_notifier_attach_attached_dev(muic_attached_dev_t new_dev)
+{
+       pr_info("%s: (%d)\n", __func__, new_dev);
+
+       __set_noti_cxt(MUIC_NOTIFY_CMD_ATTACH, new_dev);
+
+       /* muic's attached_device attach broadcast */
+       muic_notifier_notify();
+}
+
+void muic_pdic_notifier_attach_attached_dev(muic_attached_dev_t new_dev)
+{
+       pr_info("%s: (%d)\n", __func__, new_dev);
+
+#if defined(CONFIG_CCIC_S2MU004)
+       __set_ccic_noti_cxt(MUIC_PDIC_NOTIFY_CMD_ATTACH, new_dev);
+
+       /* muic's attached_device attach broadcast */
+       muic_ccic_notifier_notify();
+#else
+       __set_noti_cxt(MUIC_PDIC_NOTIFY_CMD_ATTACH, new_dev);
+
+       /* muic's attached_device attach broadcast */
+       muic_notifier_notify();
+#endif
+}
+
+void muic_pdic_notifier_detach_attached_dev(muic_attached_dev_t new_dev)
+{
+       pr_info("%s: (%d)\n", __func__, new_dev);
+
+#if defined(CONFIG_CCIC_S2MU004)
+       __set_ccic_noti_cxt(MUIC_PDIC_NOTIFY_CMD_DETACH, new_dev);
+
+       /* muic's attached_device attach broadcast */
+       muic_ccic_notifier_notify();
+#else
+       __set_noti_cxt(MUIC_PDIC_NOTIFY_CMD_DETACH, muic_notifier.attached_dev);
+       /* muic's attached_device attach broadcast */
+       muic_notifier_notify();
+#endif
+}
+
+void muic_notifier_detach_attached_dev(muic_attached_dev_t cur_dev)
+{
+       pr_info("%s: (%d)\n", __func__, cur_dev);
+
+       __set_noti_cxt(MUIC_NOTIFY_CMD_DETACH, -1);
+
+#if defined(CONFIG_MUIC_MANAGER) && defined(CONFIG_CCIC_NOTIFIER)
+       if (muic_notifier.cxt.cable_type != cur_dev)
+               pr_warn("%s: attached_dev of muic_notifier(%d) != muic_data(%d)\n",
+                       __func__, muic_notifier.cxt.cable_type, cur_dev);
+
+       if (muic_notifier.cxt.cable_type != ATTACHED_DEV_NONE_MUIC) {
+               /* muic's attached_device detach broadcast */
+               muic_notifier_notify();
+       }
+#else
+       if (muic_notifier.attached_dev != cur_dev)
+               pr_warn("%s: attached_dev of muic_notifier(%d) != muic_data(%d)\n",
+                       __func__, muic_notifier.attached_dev, cur_dev);
+
+       if (muic_notifier.attached_dev != ATTACHED_DEV_NONE_MUIC) {
+               /* muic's attached_device detach broadcast */
+               muic_notifier_notify();
+       }
+#endif
+
+       __set_noti_cxt(0, ATTACHED_DEV_NONE_MUIC);
+}
+
+void muic_notifier_logically_attach_attached_dev(muic_attached_dev_t new_dev)
+{
+       pr_info("%s: (%d)\n", __func__, new_dev);
+
+       __set_noti_cxt(MUIC_NOTIFY_CMD_ATTACH, new_dev);
+
+       /* muic's attached_device attach broadcast */
+       muic_notifier_notify();
+}
+
+void muic_notifier_logically_detach_attached_dev(muic_attached_dev_t cur_dev)
+{
+       pr_info("%s: (%d)\n", __func__, cur_dev);
+
+       __set_noti_cxt(MUIC_NOTIFY_CMD_DETACH, cur_dev);
+
+       /* muic's attached_device detach broadcast */
+       muic_notifier_notify();
+
+       __set_noti_cxt(0, ATTACHED_DEV_NONE_MUIC);
+}
+
+static int __init muic_notifier_init(void)
+{
+       int ret = 0;
+
+       pr_info("%s\n", __func__);
+#if defined(CONFIG_MUIC_MANAGER) && defined(CONFIG_CCIC_NOTIFIER)
+       muic_uses_new_noti = 1;
+#endif
+       BLOCKING_INIT_NOTIFIER_HEAD(&(muic_notifier.notifier_call_chain));
+       __set_noti_cxt(0, ATTACHED_DEV_UNKNOWN_MUIC);
+#if defined(CONFIG_CCIC_S2MU004)
+       BLOCKING_INIT_NOTIFIER_HEAD(&(muic_ccic_notifier.notifier_call_chain));
+       __set_ccic_noti_cxt(0, ATTACHED_DEV_UNKNOWN_MUIC);
+       muic_ccic_uses_new_noti = 1;
+#endif
+
+#if IS_ENABLED(CONFIG_SEC_FACTORY)
+       ret = switch_dev_register(&switch_muic_dev);
+       if (ret < 0)
+               pr_err("%s: Failed to register attached_muic_cable switch(%d)\n",
+                               __func__, ret);
+#endif
+       return ret;
+}
+device_initcall(muic_notifier_init);
+
diff --git a/drivers/muic/s2mu004-muic-sysfs.c b/drivers/muic/s2mu004-muic-sysfs.c
new file mode 100644 (file)
index 0000000..24eddb6
--- /dev/null
@@ -0,0 +1,713 @@
+/*
+ * driver/muic/s2mu004-muic_sysfs.c - S2MU004 micro USB switch device driver
+ *
+ * Copyright (C) 2015 Samsung Electronics
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+ #define pr_fmt(fmt)   "[MUIC] " fmt
+
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+
+#include <linux/mfd/samsung/s2mu004-private.h>
+#include <linux/muic/muic_core.h>
+#include <linux/muic/s2mu004-muic.h>
+#include <linux/sec_sysfs.h>
+
+static ssize_t s2mu004_muic_show_uart_en(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       struct s2mu004_muic_data *muic_data = dev_get_drvdata(dev);
+       struct muic_platform_data *muic_pdata = muic_data->pdata;
+       int ret = 0;
+
+       if (!muic_pdata->is_rustproof) {
+               pr_info("%s UART ENABLE\n",  __func__);
+               ret = sprintf(buf, "1\n");
+       } else {
+               pr_info("%s UART DISABLE\n",  __func__);
+               ret = sprintf(buf, "0\n");
+       }
+
+       return ret;
+}
+
+static ssize_t s2mu004_muic_set_uart_en(struct device *dev,
+       struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct s2mu004_muic_data *muic_data = dev_get_drvdata(dev);
+       struct muic_platform_data *muic_pdata = muic_data->pdata;
+
+       if (!strncmp(buf, "1", 1))
+               muic_pdata->is_rustproof = false;
+       else if (!strncmp(buf, "0", 1))
+               muic_pdata->is_rustproof = true;
+       else
+               pr_info("%s invalid value\n",  __func__);
+
+       pr_info("%s uart_en(%d)\n",
+               __func__, !muic_pdata->is_rustproof);
+
+       return count;
+}
+
+static ssize_t s2mu004_muic_show_uart_sel(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       struct s2mu004_muic_data *muic_data = dev_get_drvdata(dev);
+       struct muic_platform_data *pdata = muic_data->pdata;
+       const char *mode = "UNKNOWN\n";
+
+       switch (pdata->uart_path) {
+       case MUIC_PATH_UART_AP:
+               mode = "AP\n";
+               break;
+       case MUIC_PATH_UART_CP:
+               mode = "CP\n";
+               break;
+       default:
+               break;
+       }
+
+       pr_info("%s %s", __func__, mode);
+       return snprintf(buf, strlen(mode) + 1, "%s", mode);
+}
+
+static ssize_t s2mu004_muic_set_uart_sel(struct device *dev,
+       struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct s2mu004_muic_data *muic_data = dev_get_drvdata(dev);
+       struct muic_platform_data *pdata = muic_data->pdata;
+       struct muic_interface_t *muic_if = muic_data->if_data;
+
+       if (!strncasecmp(buf, "AP", 2))
+               pdata->uart_path = MUIC_PATH_UART_AP;
+       else if (!strncasecmp(buf, "CP", 2))
+               pdata->uart_path = MUIC_PATH_UART_CP;
+       else
+               pr_warn("%s invalid value\n", __func__);
+
+       muic_if->set_switch_to_uart(muic_data);
+
+       pr_info("%s %s\n", __func__, buf);
+
+       return count;
+}
+
+static ssize_t s2mu004_muic_show_usb_sel(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       return sprintf(buf, "PDA\n");
+}
+
+static ssize_t s2mu004_muic_set_usb_sel(struct device *dev,
+       struct device_attribute *attr, const char *buf, size_t count)
+{
+       return count;
+}
+
+static ssize_t s2mu004_muic_show_usb_en(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       struct s2mu004_muic_data *muic_data = dev_get_drvdata(dev);
+       struct muic_platform_data *muic_pdata = muic_data->pdata;
+
+       return sprintf(buf, "%s attached_dev = %d\n",
+               __func__, muic_pdata->attached_dev);
+}
+
+static ssize_t s2mu004_muic_set_usb_en(struct device *dev,
+       struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct s2mu004_muic_data *muic_data = dev_get_drvdata(dev);
+       struct muic_platform_data *muic_pdata = muic_data->pdata;
+       muic_attached_dev_t new_dev = ATTACHED_DEV_USB_MUIC;
+
+       if (!strncasecmp(buf, "1", 1))
+               muic_core_handle_attach(muic_data->pdata, new_dev, 0, 0);
+       else if (!strncasecmp(buf, "0", 1))
+               muic_core_handle_detach(muic_data->pdata);
+       else
+               pr_info("%s invalid value\n", __func__);
+
+       pr_info("%s attached_dev(%d)\n",
+               __func__, muic_pdata->attached_dev);
+
+       return count;
+}
+
+static ssize_t s2mu004_muic_show_adc(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       struct s2mu004_muic_data *muic_data = dev_get_drvdata(dev);
+#if IS_ENABLED(CONFIG_CCIC_S2MU004)
+       struct muic_platform_data *muic_pdata = muic_data->pdata;
+#endif
+       int ret;
+
+       mutex_lock(&muic_data->muic_mutex);
+
+#if IS_ENABLED(CONFIG_CCIC_S2MU004)
+       /* TODO: NOTE: There are abnormal operations of rising volatage AFC 9V
+        * by RID enable/disable in the s2mu004_muic_refresh_adc functions in the
+        * factory bianary. This is to minimize unnecessary interrupt by RID
+        * enable/disable whenever reading adc sysfs node
+        */
+       if (muic_pdata->is_factory_start && muic_pdata->attached_dev == 0) {
+               /* No cable detection means RID open */
+               ret = ADC_OPEN;
+       } else {
+#if IS_ENABLED(CONFIG_MUIC_S2MU004_HV)
+               if (muic_pdata->is_factory_start && muic_data->is_afc_muic_ready)
+                       /* No need to read adc in the middle of afc detection sequences */
+                       ret = ADC_GND;
+               else
+#endif
+                       ret = s2mu004_muic_refresh_adc(muic_data);
+       }
+#if IS_ENABLED(CONFIG_MUIC_S2MU004_HV)
+       pr_info("%s: factory: %d attached_dev: %d afc ready: %d", __func__,
+                       muic_pdata->is_factory_start, muic_pdata->attached_dev,
+                       muic_data->is_afc_muic_ready);
+#endif
+#else
+       ret = s2mu004_i2c_read_byte(muic_data->i2c, S2MU004_REG_MUIC_ADC);
+#endif
+
+       mutex_unlock(&muic_data->muic_mutex);
+       if (ret < 0) {
+               pr_err("%s err read adc reg(%d)\n",
+                       __func__, ret);
+               return sprintf(buf, "UNKNOWN\n");
+       }
+
+       return sprintf(buf, "%x\n", (ret & ADC_MASK));
+}
+
+static ssize_t s2mu004_muic_show_usb_state(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       struct s2mu004_muic_data *muic_data = dev_get_drvdata(dev);
+       struct muic_platform_data *muic_pdata = muic_data->pdata;
+       static unsigned long swtich_slot_time;
+
+       if (printk_timed_ratelimit(&swtich_slot_time, 5000))
+               pr_info("%s muic_pdata->attached_dev(%d)\n",
+                       __func__, muic_pdata->attached_dev);
+
+       switch (muic_pdata->attached_dev) {
+       case ATTACHED_DEV_USB_MUIC:
+       case ATTACHED_DEV_CDP_MUIC:
+       case ATTACHED_DEV_JIG_USB_OFF_MUIC:
+       case ATTACHED_DEV_JIG_USB_ON_MUIC:
+               return sprintf(buf, "USB_STATE_CONFIGURED\n");
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+#if IS_ENABLED(DEBUG_MUIC)
+static ssize_t s2mu004_muic_show_mansw(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       struct s2mu004_muic_data *muic_data = dev_get_drvdata(dev);
+       int ret;
+
+       mutex_lock(&muic_data->muic_mutex);
+       ret = s2mu004_i2c_read_byte(muic_data->i2c, S2MU004_REG_MUIC_SW_CTRL);
+       mutex_unlock(&muic_data->muic_mutex);
+
+       pr_info("%s ret:%d buf%s\n", __func__, ret, buf);
+
+       if (ret < 0) {
+               pr_err("%s: fail to read muic reg\n", __func__);
+               return sprintf(buf, "UNKNOWN\n");
+       }
+       return sprintf(buf, "0x%x\n", ret);
+}
+static ssize_t s2mu004_muic_show_interrupt_status(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       struct s2mu004_muic_data *muic_data = dev_get_drvdata(dev);
+       int st1, st2;
+
+       mutex_lock(&muic_data->muic_mutex);
+       st1 = s2mu004_i2c_read_byte(muic_data->i2c, S2MU004_REG_MUIC_INT1);
+       st2 = s2mu004_i2c_read_byte(muic_data->i2c, S2MU004_REG_MUIC_INT2);
+       mutex_unlock(&muic_data->muic_mutex);
+
+       pr_info("%s st1:0x%x st2:0x%x buf%s\n", __func__, st1, st2, buf);
+
+       if (st1 < 0 || st2 < 0) {
+               pr_err("%s: fail to read muic reg\n", __func__);
+               return sprintf(buf, "UNKNOWN\n");
+       }
+       return sprintf(buf, "st1:0x%x st2:0x%x\n", st1, st2);
+}
+static ssize_t s2mu004_muic_show_registers(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       struct s2mu004_muic_data *muic_data = dev_get_drvdata(dev);
+       char mesg[256] = "";
+
+       mutex_lock(&muic_data->muic_mutex);
+       s2mu004_read_reg_dump(muic_data, mesg);
+       mutex_unlock(&muic_data->muic_mutex);
+       pr_info("%s:%s\n", __func__, mesg);
+
+       return sprintf(buf, "%s\n", mesg);
+}
+#endif
+
+#if IS_ENABLED(CONFIG_USB_HOST_NOTIFY)
+static ssize_t s2mu004_muic_show_otg_test(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       struct s2mu004_muic_data *muic_data = dev_get_drvdata(dev);
+       int ret;
+       u8 val = 0;
+
+       mutex_lock(&muic_data->muic_mutex);
+       ret = s2mu004_i2c_read_byte(muic_data->i2c,
+               S2MU004_REG_MUIC_INT2_MASK);
+       mutex_unlock(&muic_data->muic_mutex);
+
+       if (ret < 0) {
+               pr_err("%s: fail to read muic reg\n", __func__);
+               return sprintf(buf, "UNKNOWN\n");
+       }
+
+       pr_info("%s ret:%d val:%x buf%s\n",
+               __func__, ret, val, buf);
+
+       val &= INT_VBUS_ON_MASK;
+       return sprintf(buf, "%x\n", val);
+}
+
+static ssize_t s2mu004_muic_set_otg_test(struct device *dev,
+       struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct s2mu004_muic_data *muic_data = dev_get_drvdata(dev);
+       struct muic_platform_data *muic_pdata = muic_data->pdata;
+
+       pr_info("%s buf:%s\n", __func__, buf);
+
+       /*
+        *      The otg_test is set 0 durring the otg test. Not 1 !!!
+        */
+
+       if (!strncmp(buf, "0", 1)) {
+               muic_pdata->is_otg_test = 1;
+#ifdef CONFIG_SEC_FACTORY
+               s2mu004_muic_set_otg_reg(muic_data, 1);
+#endif
+       } else if (!strncmp(buf, "1", 1)) {
+               muic_pdata->is_otg_test = 0;
+#ifdef CONFIG_SEC_FACTORY
+               s2mu004_muic_set_otg_reg(muic_data, 0);
+#endif
+       } else {
+               pr_info("%s Wrong command\n", __func__);
+               return count;
+       }
+
+       return count;
+}
+#endif
+
+static ssize_t s2mu004_muic_show_attached_dev(struct device *dev,
+        struct device_attribute *attr, char *buf)
+{
+       struct s2mu004_muic_data *muic_data = dev_get_drvdata(dev);
+       struct muic_platform_data *muic_pdata = muic_data->pdata;
+       int mdev = muic_pdata->attached_dev;
+
+       pr_info("%s attached_dev:%d\n", __func__, mdev);
+
+       switch (mdev) {
+       case ATTACHED_DEV_NONE_MUIC:
+               return sprintf(buf, "No VPS\n");
+       case ATTACHED_DEV_USB_MUIC:
+               return sprintf(buf, "USB\n");
+       case ATTACHED_DEV_CDP_MUIC:
+               return sprintf(buf, "CDP\n");
+       case ATTACHED_DEV_OTG_MUIC:
+               return sprintf(buf, "OTG\n");
+       case ATTACHED_DEV_TA_MUIC:
+               return sprintf(buf, "TA\n");
+       case ATTACHED_DEV_JIG_UART_OFF_MUIC:
+               return sprintf(buf, "JIG UART OFF\n");
+       case ATTACHED_DEV_JIG_UART_OFF_VB_MUIC:
+               return sprintf(buf, "JIG UART OFF/VB\n");
+       case ATTACHED_DEV_JIG_UART_ON_MUIC:
+               return sprintf(buf, "JIG UART ON\n");
+       case ATTACHED_DEV_JIG_UART_ON_VB_MUIC:
+               return sprintf(buf, "JIG UART ON/VB\n");
+       case ATTACHED_DEV_JIG_USB_OFF_MUIC:
+               return sprintf(buf, "JIG USB OFF\n");
+       case ATTACHED_DEV_JIG_USB_ON_MUIC:
+               return sprintf(buf, "JIG USB ON\n");
+       case ATTACHED_DEV_DESKDOCK_MUIC:
+               return sprintf(buf, "DESKDOCK\n");
+       case ATTACHED_DEV_AUDIODOCK_MUIC:
+               return sprintf(buf, "AUDIODOCK\n");
+       case ATTACHED_DEV_CHARGING_CABLE_MUIC:
+               return sprintf(buf, "PS CABLE\n");
+       case ATTACHED_DEV_AFC_CHARGER_5V_MUIC:
+       case ATTACHED_DEV_AFC_CHARGER_9V_MUIC:
+       case ATTACHED_DEV_QC_CHARGER_5V_MUIC:
+       case ATTACHED_DEV_QC_CHARGER_9V_MUIC:
+               return sprintf(buf, "AFC Charger\n");
+       default:
+               break;
+       }
+
+       return sprintf(buf, "UNKNOWN\n");
+}
+
+static ssize_t s2mu004_muic_show_audio_path(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       return 0;
+}
+
+static ssize_t s2mu004_muic_set_audio_path(struct device *dev,
+       struct device_attribute *attr, const char *buf, size_t count)
+{
+       return 0;
+}
+
+static ssize_t s2mu004_muic_show_apo_factory(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       struct s2mu004_muic_data *muic_data = dev_get_drvdata(dev);
+       struct muic_platform_data *muic_pdata = muic_data->pdata;
+       const char *mode;
+
+       /* true: Factory mode, false: not Factory mode */
+       if (muic_pdata->is_factory_start)
+               mode = "FACTORY_MODE";
+       else
+               mode = "NOT_FACTORY_MODE";
+
+       pr_info("%s : %s\n",
+               __func__, mode);
+
+       return sprintf(buf, "%s\n", mode);
+}
+
+static ssize_t s2mu004_muic_set_apo_factory(struct device *dev,
+       struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct s2mu004_muic_data *muic_data = dev_get_drvdata(dev);
+       struct muic_platform_data *muic_pdata = muic_data->pdata;
+
+       /* "FACTORY_START": factory mode */
+       if (!strncmp(buf, "FACTORY_START", 13))
+               muic_pdata->is_factory_start = true;
+       else
+               pr_info("%s Wrong command\n",  __func__);
+
+       return count;
+}
+
+static ssize_t muic_show_vbus_value(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       struct s2mu004_muic_data *muic_data = dev_get_drvdata(dev);
+       struct muic_platform_data *muic_pdata = muic_data->pdata;
+       struct i2c_client *i2c = muic_data->i2c;
+       int val = 0;
+       u8 ret, vbadc, afc_ctrl = 0;
+
+       if ((muic_pdata->attached_dev != ATTACHED_DEV_AFC_CHARGER_9V_MUIC)
+               & (muic_pdata->attached_dev != ATTACHED_DEV_QC_CHARGER_9V_MUIC)) {
+               /* Set AFC_EN, VB_ADC_EN to true in case of did not prepared afc
+                * for PD charger or Normal TA with flag afc_disable
+                */
+               pr_info("%s: Set AFC_EN, VB_ADC_ON\n", __func__);
+               afc_ctrl = s2mu004_i2c_read_byte(i2c, S2MU004_REG_AFC_CTRL1);
+               afc_ctrl |= (MUIC_AFC_CTRL1_AFC_EN_MASK|MUIC_AFC_CTRL1_VB_ADC_EN_MASK);
+               s2mu004_i2c_write_byte(i2c, S2MU004_REG_AFC_CTRL1, afc_ctrl);
+               mdelay(10);
+               afc_ctrl = s2mu004_i2c_read_byte(i2c, S2MU004_REG_AFC_CTRL1);
+       }
+
+       /* Read VBADC : must be read after afc prepared */
+       ret = s2mu004_i2c_read_byte(i2c, S2MU004_REG_AFC_STATUS);
+       if (ret < 0)
+               pr_err("%s err read AFC STATUS(0x%2x)\n", __func__, ret);
+
+       vbadc = ret & STATUS_VBADC_MASK;
+       pr_info("%s vbadc:0x%x, afc_ctrl:0x%x cable:(%d)\n",
+                       __func__, vbadc, afc_ctrl, muic_pdata->attached_dev);
+
+       switch (vbadc) {
+       case VBADC_5_3V:
+       case VBADC_5_7V_6_3V:
+               val = 5;
+               break;
+       case VBADC_8_7V_9_3V:
+       case VBADC_9_7V_10_3V:
+               val = 9;
+               break;
+       default:
+               break;
+       }
+
+       /* NOTE: If enter this statement with 9V, voltage would be decreased to 5V */
+       if ((muic_pdata->attached_dev != ATTACHED_DEV_AFC_CHARGER_9V_MUIC) &
+               (muic_pdata->attached_dev != ATTACHED_DEV_QC_CHARGER_9V_MUIC)) {
+               /* Clear AFC_EN, VB_ADC_EN  */
+               pr_info("%s: Clear AFC_EN, VB_ADC_ON\n", __func__);
+               afc_ctrl = s2mu004_i2c_read_byte(i2c, S2MU004_REG_AFC_CTRL1);
+               afc_ctrl &= ~(MUIC_AFC_CTRL1_AFC_EN_MASK|MUIC_AFC_CTRL1_VB_ADC_EN_MASK);
+               s2mu004_i2c_write_byte(i2c, S2MU004_REG_AFC_CTRL1, afc_ctrl);
+               afc_ctrl = s2mu004_i2c_read_byte(i2c, S2MU004_REG_AFC_CTRL1);
+       }
+
+       pr_info("%s VBUS:%d, afc_ctrl:0x%x\n", __func__, val, afc_ctrl);
+
+       if (val > 0)
+               return sprintf(buf, "%dV\n", val);
+
+       return sprintf(buf, "UNKNOWN\n");
+}
+
+#if IS_ENABLED(CONFIG_MUIC_S2MU004_HV)
+static ssize_t s2mu004_muic_show_afc_disable(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       struct s2mu004_muic_data *muic_data = dev_get_drvdata(dev);
+       struct muic_platform_data *pdata = muic_data->pdata;
+
+       if (pdata->afc_disable) {
+               pr_info("%s AFC DISABLE\n", __func__);
+               return sprintf(buf, "1\n");
+       }
+
+       pr_info("%s AFC ENABLE", __func__);
+       return sprintf(buf, "0\n");
+}
+
+static ssize_t s2mu004_muic_set_afc_disable(struct device *dev,
+       struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct s2mu004_muic_data *muic_data = dev_get_drvdata(dev);
+       struct muic_platform_data *pdata = muic_data->pdata;
+       bool curr_val = pdata->afc_disable;
+       int param_val, ret = 0;
+#if IS_ENABLED(CONFIG_MUIC_MANAGER)
+       struct muic_interface_t *muic_if = (struct muic_interface_t *)muic_data->if_data;
+       int mdev = 0;
+#endif /* CONFIG_MUIC_MANAGER */
+
+       mutex_lock(&muic_data->muic_mutex);
+
+       if (!strncasecmp(buf, "1", 1))
+               pdata->afc_disable = true;
+       else if (!strncasecmp(buf, "0", 1))
+               pdata->afc_disable = false;
+       else
+               pr_warn("%s invalid value\n", __func__);
+
+#if IS_ENABLED(CONFIG_MUIC_MANAGER)
+       param_val = pdata->afc_disable ? '1' : '0';
+#endif
+
+#ifdef CM_OFFSET
+       ret = sec_set_param(CM_OFFSET + 1, (char)param_val);
+       if (ret < 0) {
+               pr_err("%s:set_param failed - %02x:%02x(%d)\n",
+                       __func__, param_val, curr_val, ret);
+               pdata->afc_disable = curr_val;
+               return ret;
+       }
+#else
+       pr_err("%s:set_param is NOT supported! - %02x:%02x(%d)\n",
+               __func__, param_val, curr_val, ret);
+#endif
+
+       pr_info("%s afc_disable(%d)\n", __func__, pdata->afc_disable);
+
+       /* FIXME: for factory self charging test (AFC-> NORMAL TA) */
+#ifdef CONFIG_MUIC_MANAGER
+       mdev = muic_manager_get_legacy_dev(muic_if);
+
+       /* In LCiA charge test, There would be abnormal uart disconnection in
+        * case of afc_disable (0) after 619K insertion. With 619K JIG, there
+        * should be no re-detect chg interrupt which makes muic path open
+        */
+       if (pdata->is_factory_start && (mdev != ATTACHED_DEV_JIG_UART_ON_MUIC)) {
+               /* Do not enter with 619K in FAC binary */
+               pr_info("%s re-detect chg (mdev:%d)\n", __func__, mdev);
+
+               MUIC_PDATA_FUNC(muic_if->reset_afc_register, muic_data, &ret);
+
+               MUIC_PDATA_FUNC_MULTI_PARAM(muic_if->control_rid_adc,
+                       muic_data, S2MU004_ENABLE, &ret);
+               mdelay(50);
+
+               MUIC_PDATA_FUNC_MULTI_PARAM(muic_if->control_rid_adc,
+                       muic_data, S2MU004_DISABLE, &ret);
+               mdelay(50);
+
+               MUIC_PDATA_FUNC(muic_if->bcd_rescan, muic_data, &ret);
+               pr_info("%s re-detect chg done\n", __func__);
+
+               /* TODO: When closing charge test, there would be afc_disable 0
+                * It needs to set is_afc_muic_ready to false in order to
+                * detect afc 9V again
+                */
+               if (!pdata->afc_disable) {
+                       pr_info("%s detach for 9V re-detection\n", __func__);
+                       muic_core_handle_detach(muic_data->pdata);
+               }
+               /* Needs to 150ms after rescan */
+               mdelay(150);
+       }
+#else
+       if (muic_data->is_factory_start)
+               pr_info("%s re-detect chg\n", __func__);
+
+       muic_core_handle_detach(muic_data->pdata);
+       s2mu004_muic_detect_dev(muic_data);
+#endif
+
+       mutex_unlock(&muic_data->muic_mutex);
+
+       return count;
+}
+
+#if IS_ENABLED(CONFIG_HV_MUIC_VOLTAGE_CTRL)
+static ssize_t muic_store_afc_set_voltage(struct device *dev,
+       struct device_attribute *attr, const char *buf, size_t count)
+{
+       if (!strncasecmp(buf, "5V", 2))
+               hv_muic_change_afc_voltage(MUIC_HV_5V);
+       else if (!strncasecmp(buf, "9V", 2))
+               hv_muic_change_afc_voltage(MUIC_HV_9V);
+       else
+               pr_warn("%s invalid value : %s\n", __func__, buf);
+
+       return count;
+}
+#endif /* CONFIG_HV_MUIC_VOLTAGE_CTRL */
+#endif /* CONFIG_MUIC_S2MU004_HV */
+
+static DEVICE_ATTR(uart_en, 0664, s2mu004_muic_show_uart_en,
+                                       s2mu004_muic_set_uart_en);
+static DEVICE_ATTR(uart_sel, 0664, s2mu004_muic_show_uart_sel,
+                                       s2mu004_muic_set_uart_sel);
+static DEVICE_ATTR(usb_sel, 0664, s2mu004_muic_show_usb_sel,
+                                       s2mu004_muic_set_usb_sel);
+static DEVICE_ATTR(adc, 0664, s2mu004_muic_show_adc, NULL);
+#if IS_ENABLED(DEBUG_MUIC)
+static DEVICE_ATTR(mansw, 0664, s2mu004_muic_show_mansw, NULL);
+static DEVICE_ATTR(dump_registers, 0664, s2mu004_muic_show_registers, NULL);
+static DEVICE_ATTR(int_status, 0664, s2mu004_muic_show_interrupt_status, NULL);
+#endif
+static DEVICE_ATTR(usb_state, 0664, s2mu004_muic_show_usb_state, NULL);
+#if IS_ENABLED(CONFIG_USB_HOST_NOTIFY)
+static DEVICE_ATTR(otg_test, 0664,
+               s2mu004_muic_show_otg_test, s2mu004_muic_set_otg_test);
+#endif
+static DEVICE_ATTR(attached_dev, 0664, s2mu004_muic_show_attached_dev, NULL);
+static DEVICE_ATTR(audio_path, 0664,
+               s2mu004_muic_show_audio_path, s2mu004_muic_set_audio_path);
+static DEVICE_ATTR(apo_factory, 0664,
+               s2mu004_muic_show_apo_factory,
+               s2mu004_muic_set_apo_factory);
+static DEVICE_ATTR(usb_en, 0664,
+               s2mu004_muic_show_usb_en,
+               s2mu004_muic_set_usb_en);
+static DEVICE_ATTR(vbus_value, 0444, muic_show_vbus_value, NULL);
+#if IS_ENABLED(CONFIG_MUIC_S2MU004_HV)
+static DEVICE_ATTR(afc_disable, 0664,
+               s2mu004_muic_show_afc_disable, s2mu004_muic_set_afc_disable);
+#if IS_ENABLED(CONFIG_HV_MUIC_VOLTAGE_CTRL)
+static DEVICE_ATTR(afc_set_voltage, 0220,
+               NULL, muic_store_afc_set_voltage);
+#endif /* CONFIG_HV_MUIC_VOLTAGE_CTRL */
+#endif /* CONFIG_MUIC_S2MU004_HV */
+
+static struct attribute *s2mu004_muic_attributes[] = {
+       &dev_attr_uart_en.attr,
+       &dev_attr_uart_sel.attr,
+       &dev_attr_usb_sel.attr,
+       &dev_attr_adc.attr,
+#if IS_ENABLED(DEBUG_MUIC)
+       &dev_attr_mansw.attr,
+       &dev_attr_dump_registers.attr,
+       &dev_attr_int_status.attr,
+#endif
+       &dev_attr_usb_state.attr,
+#if IS_ENABLED(CONFIG_USB_HOST_NOTIFY)
+       &dev_attr_otg_test.attr,
+#endif
+       &dev_attr_attached_dev.attr,
+       &dev_attr_audio_path.attr,
+       &dev_attr_apo_factory.attr,
+       &dev_attr_usb_en.attr,
+       &dev_attr_vbus_value.attr,
+#if IS_ENABLED(CONFIG_MUIC_S2MU004_HV)
+       &dev_attr_afc_disable.attr,
+#if IS_ENABLED(CONFIG_HV_MUIC_VOLTAGE_CTRL)
+       &dev_attr_afc_set_voltage.attr,
+#endif /* CONFIG_HV_MUIC_VOLTAGE_CTRL */
+#endif /* CONFIG_MUIC_S2MU004_HV */
+       NULL
+};
+
+static const struct attribute_group s2mu004_muic_group = {
+       .attrs = s2mu004_muic_attributes,
+};
+
+int s2mu004_muic_init_sysfs(struct s2mu004_muic_data *muic_data)
+{
+       int ret;
+       /* create sysfs group */
+#if IS_ENABLED(CONFIG_SEC_FACTORY)
+       muic_data->switch_device = sec_device_find("switch");
+#endif
+
+       if (muic_data->switch_device == NULL)
+               muic_data->switch_device = sec_device_create(NULL, "switch");
+
+       if (IS_ERR(muic_data->switch_device)) {
+               pr_err("%s Failed to create device(switch)!\n", __func__);
+               ret = -ENODEV;
+               return ret;
+       }
+
+       ret = sysfs_create_group(&muic_data->switch_device->kobj, &s2mu004_muic_group);
+       if (ret) {
+               pr_err("failed to create sysfs\n");
+               return ret;
+       }
+       dev_set_drvdata(muic_data->switch_device, muic_data);
+       return ret;
+}
+
+void s2mu004_muic_deinit_sysfs(struct s2mu004_muic_data *muic_data)
+{
+       if (muic_data->switch_device)
+               sysfs_remove_group(&muic_data->switch_device->kobj, &s2mu004_muic_group);
+}
diff --git a/drivers/muic/s2mu004-muic.c b/drivers/muic/s2mu004-muic.c
new file mode 100644 (file)
index 0000000..4ce270d
--- /dev/null
@@ -0,0 +1,2192 @@
+/*
+ * driver/muic/s2mu004.c - S2MU004 micro USB switch device driver
+ *
+ * Copyright (C) 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
+ *
+ */
+#define pr_fmt(fmt)    "[MUIC] " fmt
+
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/muic/muic_core.h>
+#include <linux/mfd/samsung/s2mu004.h>
+#include <linux/mfd/samsung/s2mu004-private.h>
+#include <linux/muic/s2mu004-muic.h>
+#include <linux/muic/s2mu004-muic-sysfs.h>
+
+#include <linux/power_supply.h>
+#include <linux/muic/s2mu004-muic-hv.h>
+
+#include <linux/muic/s2mu004-muic-notifier.h>
+#if defined(CONFIG_USB_HOST_NOTIFY)
+#include <linux/usb_notify.h>
+#endif
+#include <linux/muic/muic_interface.h>
+
+#if IS_ENABLED(CONFIG_CCIC_NOTIFIER)
+#include <linux/ccic/ccic_notifier.h>
+#endif
+#if IS_ENABLED(CONFIG_VBUS_NOTIFIER)
+#include <linux/vbus_notifier.h>
+#endif
+#if IS_ENABLED(CONFIG_MUIC_UART_SWITCH)
+#include <mach/pinctrl-samsung.h>
+#endif
+#if IS_ENABLED(CONFIG_CP_UART_NOTI)
+#include <soc/samsung/exynos-modem-ctrl.h>
+#endif
+
+static const char *dev_to_str(muic_attached_dev_t n)
+{
+       char *ret;
+
+       switch (n) {
+       ENUM_STR(ATTACHED_DEV_NONE_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_USB_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_CDP_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_OTG_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_TA_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_UNOFFICIAL_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_UNOFFICIAL_TA_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_UNOFFICIAL_ID_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_UNOFFICIAL_ID_TA_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_UNOFFICIAL_ID_ANY_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_UNOFFICIAL_ID_USB_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_UNOFFICIAL_ID_CDP_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_UNDEFINED_CHARGING_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_DESKDOCK_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_UNKNOWN_VB_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_DESKDOCK_VB_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_CARDOCK_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_JIG_UART_OFF_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_JIG_UART_OFF_VB_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_JIG_UART_OFF_VB_OTG_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_JIG_UART_OFF_VB_FG_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_JIG_UART_ON_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_JIG_UART_ON_VB_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_JIG_USB_OFF_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_JIG_USB_ON_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_JIG_RID_OPEN_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_SMARTDOCK_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_SMARTDOCK_VB_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_SMARTDOCK_TA_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_SMARTDOCK_USB_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_UNIVERSAL_MMDOCK_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_AUDIODOCK_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_MHL_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_CHARGING_CABLE_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_AFC_CHARGER_PREPARE_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_AFC_CHARGER_PREPARE_DUPLI_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_AFC_CHARGER_5V_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_AFC_CHARGER_5V_DUPLI_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_AFC_CHARGER_9V_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_AFC_CHARGER_ERR_V_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_AFC_CHARGER_ERR_V_DUPLI_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_QC_CHARGER_PREPARE_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_QC_CHARGER_5V_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_QC_CHARGER_ERR_V_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_QC_CHARGER_9V_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_HV_ID_ERR_UNDEFINED_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_HV_ID_ERR_UNSUPPORTED_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_HV_ID_ERR_SUPPORTED_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_HMT_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_VZW_ACC_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_VZW_INCOMPATIBLE_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_USB_LANHUB_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_TYPE2_CHG_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_TYPE3_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_TYPE3_MUIC_TA, ret);
+       ENUM_STR(ATTACHED_DEV_TYPE3_ADAPTER_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_TYPE3_CHARGER_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_NONE_TYPE3_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_UNSUPPORTED_ID_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_UNSUPPORTED_ID_VB_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_TIMEOUT_OPEN_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_WIRELESS_PAD_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_POWERPACK_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_UNDEFINED_RANGE_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_WATER_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_CHK_WATER_REQ, ret);
+       ENUM_STR(ATTACHED_DEV_CHK_WATER_DRY_REQ, ret);
+       ENUM_STR(ATTACHED_DEV_GAMEPAD_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_CHECK_OCP, ret);
+       ENUM_STR(ATTACHED_DEV_RDU_TA_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_AFC_CHARGER_9V_DUPLI_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_UNKNOWN_MUIC, ret);
+       ENUM_STR(ATTACHED_DEV_NUM, ret);
+       default:
+               return "invalid";
+       }
+       return ret;
+}
+
+static struct s2mu004_muic_data *static_data;
+
+static int s2mu004_muic_set_com_sw(struct s2mu004_muic_data *muic_data,
+               u8 reg_val);
+static void s2mu004_muic_detect_dev_ccic(struct s2mu004_muic_data *muic_data,
+               muic_attached_dev_t new_dev);
+#if IS_ENABLED(CONFIG_MUIC_S2MU004_RID)
+static int s2mu004_i2c_update_bit(struct i2c_client *client,
+                                 u8 reg, u8 mask, u8 shift, u8 value);
+#endif /* CONFIG_MUIC_S2MU004_RID */
+
+#if IS_ENABLED(DEBUG_MUIC)
+#define MAX_LOG 25
+#define READ 0
+#define WRITE 1
+
+static u8 s2mu004_log_cnt;
+static u8 s2mu004_log[MAX_LOG][3];
+
+static void s2mu004_reg_log(u8 reg, u8 value, u8 rw)
+{
+       s2mu004_log[s2mu004_log_cnt][0] = reg;
+       s2mu004_log[s2mu004_log_cnt][1] = value;
+       s2mu004_log[s2mu004_log_cnt][2] = rw;
+       s2mu004_log_cnt++;
+       if (s2mu004_log_cnt >= MAX_LOG)
+               s2mu004_log_cnt = 0;
+}
+
+static void s2mu004_print_reg_log(void)
+{
+       int i;
+       u8 reg, value, rw;
+       char mesg[256] = "";
+
+       for (i = 0; i < MAX_LOG; i++) {
+               reg = s2mu004_log[s2mu004_log_cnt][0];
+               value = s2mu004_log[s2mu004_log_cnt][1];
+               rw = s2mu004_log[s2mu004_log_cnt][2];
+               s2mu004_log_cnt++;
+
+               if (s2mu004_log_cnt >= MAX_LOG)
+                       s2mu004_log_cnt = 0;
+               sprintf(mesg + strlen(mesg), "%x(%x)%x ", reg, value, rw);
+       }
+       pr_info("%s %s\n", __func__, mesg);
+}
+
+void s2mu004_read_reg_dump(struct s2mu004_muic_data *muic, char *mesg)
+{
+       u8 val;
+
+       s2mu004_read_reg(muic->i2c, S2MU004_REG_MUIC_CTRL1, &val);
+       sprintf(mesg + strlen(mesg), "CTRL1:%x ", val);
+       s2mu004_read_reg(muic->i2c, S2MU004_REG_MUIC_SW_CTRL, &val);
+       sprintf(mesg + strlen(mesg), "SW_CTRL:%x ", val);
+       s2mu004_read_reg(muic->i2c, S2MU004_REG_MUIC_INT1_MASK, &val);
+       sprintf(mesg + strlen(mesg), "IM1:%x ", val);
+       s2mu004_read_reg(muic->i2c, S2MU004_REG_MUIC_INT2_MASK, &val);
+       sprintf(mesg + strlen(mesg), "IM2:%x ", val);
+       s2mu004_read_reg(muic->i2c, S2MU004_REG_MUIC_CHG_TYPE, &val);
+       sprintf(mesg + strlen(mesg), "CHG_T:%x ", val);
+       s2mu004_read_reg(muic->i2c, S2MU004_REG_MUIC_DEVICE_APPLE, &val);
+       sprintf(mesg + strlen(mesg), "APPLE_DT:%x ", val);
+       s2mu004_read_reg(muic->i2c, S2MU004_REG_MUIC_ADC, &val);
+       sprintf(mesg + strlen(mesg), "ADC:%x ", val);
+       s2mu004_read_reg(muic->i2c, S2MU004_REG_MUIC_DEVICE_TYPE1, &val);
+       sprintf(mesg + strlen(mesg), "DT1:%x ", val);
+       s2mu004_read_reg(muic->i2c, S2MU004_REG_MUIC_DEVICE_TYPE2, &val);
+       sprintf(mesg + strlen(mesg), "DT2:%x ", val);
+       s2mu004_read_reg(muic->i2c, S2MU004_REG_MUIC_DEVICE_TYPE3, &val);
+       sprintf(mesg + strlen(mesg), "DT3:%x ", val);
+}
+
+void s2mu004_print_reg_dump(struct s2mu004_muic_data *muic_data)
+{
+       char mesg[256] = "";
+
+       s2mu004_read_reg_dump(muic_data, mesg);
+
+       pr_info("%s %s\n", __func__, mesg);
+}
+#endif
+
+/* interface functions */
+static int s2mu004_if_com_to_open(void *mdata)
+{
+       struct s2mu004_muic_data *muic_data = (struct s2mu004_muic_data *)mdata;
+       int ret = 0;
+
+       mutex_lock(&muic_data->switch_mutex);
+       pr_info("%s\n", __func__);
+       ret = s2mu004_muic_com_to_open(muic_data);
+       mutex_unlock(&muic_data->switch_mutex);
+
+       return ret;
+}
+
+static int s2mu004_if_com_to_audio(void *mdata)
+{
+       struct s2mu004_muic_data *muic_data = (struct s2mu004_muic_data *)mdata;
+       int ret = 0;
+
+       mutex_lock(&muic_data->switch_mutex);
+       pr_info("%s\n", __func__);
+       ret = s2mu004_muic_com_to_audio(muic_data);
+       mutex_unlock(&muic_data->switch_mutex);
+
+       return ret;
+}
+
+static int s2mu004_if_com_to_otg(void *mdata)
+{
+       struct s2mu004_muic_data *muic_data = (struct s2mu004_muic_data *)mdata;
+       int ret = 0;
+
+       mutex_lock(&muic_data->switch_mutex);
+       pr_info("%s\n", __func__);
+       ret = s2mu004_muic_com_to_otg(muic_data);
+       mutex_unlock(&muic_data->switch_mutex);
+
+       return ret;
+}
+
+#if IS_ENABLED(CONFIG_MUIC_S2MU004_RID)
+static int s2mu004_if_get_adc(void *mdata)
+{
+       struct s2mu004_muic_data *muic_data = (struct s2mu004_muic_data *)mdata;
+       int ret = 0;
+
+       mutex_lock(&muic_data->switch_mutex);
+       pr_info("%s\n", __func__);
+       ret = s2mu004_muic_recheck_adc(muic_data);
+       mutex_unlock(&muic_data->switch_mutex);
+
+       return ret & 0x1F;
+}
+#endif
+
+static int s2mu004_if_switch_to_usb(void *mdata)
+{
+       struct s2mu004_muic_data *muic_data = (struct s2mu004_muic_data *)mdata;
+       int ret = 0;
+
+       mutex_lock(&muic_data->switch_mutex);
+       ret = s2mu004_muic_com_to_usb(muic_data);
+       if (ret)
+               pr_err("%s set_com_usb err\n", __func__);
+       mutex_unlock(&muic_data->switch_mutex);
+
+       return ret;
+}
+
+static int s2mu004_if_switch_to_uart(void *mdata)
+{
+       struct s2mu004_muic_data *muic_data = (struct s2mu004_muic_data *)mdata;
+       int ret = 0;
+
+       mutex_lock(&muic_data->switch_mutex);
+       pr_info("%s\n", __func__);
+       ret = s2mu004_muic_com_to_uart(muic_data);
+       mutex_unlock(&muic_data->switch_mutex);
+
+       return ret;
+}
+
+static int s2mu004_if_get_vbus(void *mdata)
+{
+       struct s2mu004_muic_data *muic_data = (struct s2mu004_muic_data *)mdata;
+
+       return s2mu004_muic_get_vbus_state(muic_data);
+}
+
+static void s2mu004_if_set_cable_state(void *mdata, muic_attached_dev_t new_dev)
+{
+       struct s2mu004_muic_data *muic_data = (struct s2mu004_muic_data *)mdata;
+
+       mutex_lock(&muic_data->muic_mutex);
+       wake_lock(&muic_data->wake_lock);
+
+       s2mu004_muic_detect_dev_ccic(muic_data, new_dev);
+
+       wake_unlock(&muic_data->wake_lock);
+       mutex_unlock(&muic_data->muic_mutex);
+}
+
+static void s2mu004_if_set_otg_detect_en(void *mdata, bool en)
+{
+       struct s2mu004_muic_data *muic_data = (struct s2mu004_muic_data *)mdata;
+
+       s2mu004_muic_control_vbus_det(muic_data, en);
+}
+
+static void s2mu004_if_dcd_rescan(void *mdata)
+{
+       struct s2mu004_muic_data *muic_data = (struct s2mu004_muic_data *)mdata;
+
+       mutex_lock(&muic_data->muic_mutex);
+       wake_lock(&muic_data->wake_lock);
+
+       muic_data->is_dcd_recheck = true;
+       s2mu004_muic_dcd_rescan(muic_data);
+
+       wake_unlock(&muic_data->wake_lock);
+       mutex_unlock(&muic_data->muic_mutex);
+}
+
+static int s2mu004_if_bcd_rescan(void *mdata)
+{
+       struct s2mu004_muic_data *muic_data = (struct s2mu004_muic_data *)mdata;
+
+       return s2mu004_muic_bcd_rescan(muic_data);
+}
+
+static int s2mu004_if_control_rid_adc(void *mdata, bool enable)
+{
+       struct s2mu004_muic_data *muic_data = (struct s2mu004_muic_data *)mdata;
+
+       return s2mu004_muic_control_rid_adc(muic_data, enable);
+}
+
+#if defined(CONFIG_MUIC_S2MU004_HV)
+static int s2mu004_if_set_afc_reset(void *mdata)
+{
+       struct s2mu004_muic_data *muic_data = (struct s2mu004_muic_data *)mdata;
+       struct muic_interface_t *muic_if = muic_data->if_data;
+
+       pr_info("%s\n", __func__);
+       muic_if->is_afc_reset = true;
+
+       return 0;
+}
+
+static muic_attached_dev_t s2mu004_if_check_id_err(void *mdata,
+                                                  muic_attached_dev_t new_dev)
+{
+       struct s2mu004_muic_data *muic_data = (struct s2mu004_muic_data *)mdata;
+
+       return hv_muic_check_id_err(muic_data, new_dev);
+}
+
+static int s2mu004_if_reset_hvcontrol_reg(void *mdata)
+{
+       struct s2mu004_muic_data *muic_data = (struct s2mu004_muic_data *)mdata;
+
+       s2mu004_hv_muic_reset_hvcontrol_reg(muic_data);
+
+       return 0;
+}
+
+static int s2mu004_if_check_afc_ready(void *mdata)
+{
+       struct s2mu004_muic_data *muic_data = (struct s2mu004_muic_data *)mdata;
+
+       return s2mu004_muic_check_afc_ready(muic_data);
+}
+
+static int s2mu004_if_reset_afc_register(void *mdata)
+{
+       struct s2mu004_muic_data *muic_data = (struct s2mu004_muic_data *)mdata;
+
+       return s2mu004_muic_reset_afc_register(muic_data);
+}
+#endif
+
+static int s2mu004_if_jig_on(void *mdata)
+{
+       struct s2mu004_muic_data *muic_data = (struct s2mu004_muic_data *)mdata;
+
+       return s2mu004_muic_jig_on(muic_data);
+}
+
+static int s2mu004_if_set_gpio_uart_sel(void *mdata, int uart_path)
+{
+       struct s2mu004_muic_data *muic_data = (struct s2mu004_muic_data *)mdata;
+
+       return s2mu004_set_gpio_uart_sel(muic_data, uart_path);
+}
+
+int s2mu004_i2c_read_byte(struct i2c_client *client, u8 command)
+{
+       int ret = 0;
+       int retry = 0;
+       u8 data = 0;
+
+       ret = s2mu004_read_reg(client, command, &data);
+
+       while (ret < 0) {
+               pr_info("failed to read reg(0x%x) retry(%d)\n", command, retry);
+               if (retry > 10) {
+                       pr_err("%s  retry failed!!\n", __func__);
+                       break;
+               }
+               msleep(100);
+               ret = s2mu004_read_reg(client, command, &data);
+               retry++;
+       }
+
+#if IS_ENABLED(DEBUG_MUIC)
+       s2mu004_reg_log(command, ret, retry << 1 | READ);
+#endif
+       return data;
+}
+
+int s2mu004_i2c_write_byte(struct i2c_client *client, u8 command, u8 value)
+{
+       int ret_r = 0;
+       int ret_w = 0;
+       int retry = 0;
+       u8 written = 0;
+
+       ret_w = s2mu004_write_reg(client, command, value);
+
+       while (ret_w < 0) {
+               pr_info("failed to write reg(0x%x) retry(%d)\n", command, retry);
+               ret_r = s2mu004_read_reg(client, command, &written);
+               if (ret_r < 0)
+                       pr_err("%s reg(0x%x)\n", __func__, command);
+               msleep(100);
+               ret_w = s2mu004_write_reg(client, command, value);
+               retry++;
+       }
+#if IS_ENABLED(DEBUG_MUIC)
+       s2mu004_reg_log(command, value, retry << 1 | WRITE);
+#endif
+       return ret_w;
+}
+
+static int s2mu004_i2c_guaranteed_wbyte(struct i2c_client *client,
+                                       u8 command, u8 value)
+{
+       int ret;
+       int retry = 0;
+       int written;
+
+       ret = s2mu004_i2c_write_byte(client, command, value);
+       written = s2mu004_i2c_read_byte(client, command);
+       while (written != value) {
+               pr_info("reg(0x%x): written(0x%x) != value(0x%x)\n",
+                       command, written, value);
+               if (retry > 10) {
+                       pr_err("%s  retry failed!!\n", __func__);
+                       break;
+               }
+               msleep(100);
+               retry++;
+               ret = s2mu004_i2c_write_byte(client, command, value);
+               written = s2mu004_i2c_read_byte(client, command);
+       }
+       return ret;
+}
+
+int s2mu004_muic_control_rid_adc(struct s2mu004_muic_data *muic_data, bool enable)
+{
+       int data = 0;
+
+       pr_info("%s (%s)\n", __func__, enable ? "Enable" : "Disable");
+
+       data = s2mu004_i2c_read_byte(muic_data->i2c, S2MU004_REG_MUIC_RID_CTRL);
+
+       if (enable)
+               data &= ~MUIC_CTRL2_ADC_OFF_MASK;
+       else
+               data |= MUIC_CTRL2_ADC_OFF_MASK;
+
+       s2mu004_i2c_write_byte(muic_data->i2c, S2MU004_REG_MUIC_RID_CTRL, data);
+
+       return 0;
+}
+
+int s2mu004_muic_bcd_rescan(struct s2mu004_muic_data *muic_data)
+{
+       int data = 0;
+
+       pr_info("%s\n", __func__);
+
+       /* start secondary dp dm detect */
+       data = s2mu004_i2c_read_byte(muic_data->i2c, S2MU004_REG_MUIC_BCD_RESCAN);
+       data |= MUIC_BCD_RESCAN_MASK;
+       s2mu004_i2c_write_byte(muic_data->i2c, S2MU004_REG_MUIC_BCD_RESCAN, data);
+
+       return 0;
+}
+
+#if IS_ENABLED(CONFIG_MUIC_S2MU004_HV)
+int s2mu004_muic_reset_afc_register(struct s2mu004_muic_data *muic_data)
+{
+       int data = 0;
+
+       pr_info("%s\n", __func__);
+
+       data = s2mu004_i2c_read_byte(muic_data->i2c, S2MU004_REG_AFC_LOGIC_CTRL2);
+       data |= MUIC_AFC_LOGIC_CTRL2_AFC_RST_MASK;
+       s2mu004_i2c_write_byte(muic_data->i2c, S2MU004_REG_AFC_LOGIC_CTRL2, data);
+
+       return 0;
+}
+
+static int s2mu004_muic_check_afc_ready(struct s2mu004_muic_data *muic_data)
+{
+       struct muic_platform_data *muic_pdata = muic_data->pdata;
+
+       pr_info("%s\n", __func__);
+
+       /* check muic ready for afc */
+       if (muic_pdata->afc_disable)
+               pr_info("%s AFC Disable(%d) by USER!\n",
+                       __func__, muic_pdata->afc_disable);
+       else {
+               pr_info("%s ready:%d  afc_check:%d\n", __func__,
+                       muic_data->is_afc_muic_ready, muic_data->afc_check);
+               if (muic_data->is_afc_muic_ready == false && muic_data->afc_check)
+                       s2mu004_muic_prepare_afc_charger(muic_data);
+       }
+
+       return 0;
+}
+#endif
+
+void s2mu004_muic_control_vbus_det(struct s2mu004_muic_data *muic_data, bool enable)
+{
+       int data = 0;
+
+       pr_info("%s (%s)\n", __func__, enable ? "Enable" : "Disable");
+
+       /* enable vbus det for interrupt */
+       data = s2mu004_i2c_read_byte(muic_data->i2c, S2MU004_REG_AFC_OTP6);
+
+       if (enable)
+               data |= MUIC_AFC_OTP6_EN_VBUS_DET_MUIC_MASK;
+       else
+               data &= ~MUIC_AFC_OTP6_EN_VBUS_DET_MUIC_MASK;
+
+       s2mu004_i2c_write_byte(muic_data->i2c, S2MU004_REG_AFC_OTP6, data);
+
+}
+
+#if defined(GPIO_USB_SEL)
+static int s2mu004_set_gpio_usb_sel(int uart_sel)
+{
+       return 0;
+}
+#endif /* GPIO_USB_SEL */
+
+int s2mu004_set_gpio_uart_sel(struct s2mu004_muic_data *muic_data, int uart_sel)
+{
+       const char *mode;
+       struct muic_platform_data *muic_pdata = muic_data->pdata;
+#if !IS_ENABLED(CONFIG_MUIC_UART_SWITCH)
+       int uart_sel_gpio = muic_pdata->gpio_uart_sel;
+       int uart_sel_val;
+       int ret;
+
+       ret = gpio_request(uart_sel_gpio, "GPIO_UART_SEL");
+       if (ret) {
+               pr_err("failed to gpio_request GPIO_UART_SEL\n");
+               return ret;
+       }
+
+       uart_sel_val = gpio_get_value(uart_sel_gpio);
+
+       switch (uart_sel) {
+       case MUIC_PATH_UART_AP:
+               mode = "AP_UART";
+               if (gpio_is_valid(uart_sel_gpio))
+                       gpio_direction_output(uart_sel_gpio, 1);
+               break;
+       case MUIC_PATH_UART_CP:
+               mode = "CP_UART";
+               if (gpio_is_valid(uart_sel_gpio))
+                       gpio_direction_output(uart_sel_gpio, 0);
+               break;
+       default:
+               mode = "Error";
+               break;
+       }
+
+       uart_sel_val = gpio_get_value(uart_sel_gpio);
+
+       gpio_free(uart_sel_gpio);
+
+       pr_info("%s, GPIO_UART_SEL(%d)=%c\n",
+               mode, uart_sel_gpio, (uart_sel_val == 0 ? 'L' : 'H'));
+#else
+       switch (uart_sel) {
+       case MUIC_PATH_UART_AP:
+               mode = "AP_UART";
+               pin_config_set(muic_pdata.uart_addr, muic_pdata.uart_rxd,
+                              PINCFG_PACK(PINCFG_TYPE_FUNC, 0x2));
+               pin_config_set(muic_pdata.uart_addr, muic_pdata.uart_txd,
+                              PINCFG_PACK(PINCFG_TYPE_FUNC, 0x2));
+               break;
+       case MUIC_PATH_UART_CP:
+               mode = "CP_UART";
+               pin_config_set(muic_pdata.uart_addr, muic_pdata.uart_rxd,
+                              PINCFG_PACK(PINCFG_TYPE_FUNC, 0x3));
+               pin_config_set(muic_pdata.uart_addr, muic_pdata.uart_txd,
+                              PINCFG_PACK(PINCFG_TYPE_FUNC, 0x3));
+               break;
+       default:
+               mode = "Error";
+               break;
+       }
+
+       pr_info("%s %s\n", __func__, mode);
+#endif /* CONFIG_MUIC_UART_SWITCH */
+       return 0;
+}
+
+#if IS_ENABLED(GPIO_DOC_SWITCH)
+static int s2mu004_set_gpio_doc_switch(struct s2mu004_muic_data *muic_data, int val)
+{
+       struct muic_platform_data *muic_pdata = muic_data->pdata;
+       int doc_switch_gpio = muic_pdata->gpio_doc_switch;
+       int doc_switch_val;
+       int ret;
+
+       ret = gpio_request(doc_switch_gpio, "GPIO_DOC_SWITCH");
+       if (ret) {
+               pr_err("failed to gpio_request GPIO_DOC_SWITCH\n");
+               return ret;
+       }
+
+       doc_switch_val = gpio_get_value(doc_switch_gpio);
+
+       if (gpio_is_valid(doc_switch_gpio))
+               gpio_set_value(doc_switch_gpio, val);
+       doc_switch_val = gpio_get_value(doc_switch_gpio);
+
+       gpio_free(doc_switch_gpio);
+
+       pr_info("%s (%d)%c\n", __func__,
+               doc_switch_gpio, (doc_switch_val == 0 ? 'L' : 'H'));
+
+       return 0;
+}
+#endif /* GPIO_DOC_SWITCH */
+
+#if IS_ENABLED(CONFIG_SEC_FACTORY)
+int s2mu004_muic_set_otg_reg(struct s2mu004_muic_data *muic_data, bool on)
+{
+       struct i2c_client *i2c = muic_data->i2c;
+       u8 reg_val;
+       int ret = 0;
+
+       if (on) {
+               /* enable vbus det for interrupt */
+               s2mu004_muic_control_vbus_det(muic_data, true);
+       } else {
+               /* disable vbus det for interrupt */
+               s2mu004_muic_control_vbus_det(muic_data, false);
+       }
+
+       /* 0x1e : hidden register */
+       ret = s2mu004_i2c_read_byte(i2c, 0x1e);
+       if (ret < 0)
+               pr_err("%s err read 0x1e reg(%d)\n", __func__, ret);
+
+       /* Set 0x1e[5:4] bit to 0x11 or 0x01 */
+       if (on)
+               reg_val = ret | (0x1 << 5);
+       else
+               reg_val = ret & ~(0x1 << 5);
+
+       if (reg_val ^ ret) {
+               pr_info("%s 0x%x != 0x%x, update\n", __func__, reg_val, ret);
+
+               ret = s2mu004_i2c_guaranteed_wbyte(i2c, 0x1e, reg_val);
+               if (ret < 0)
+                       pr_err("%s err write(%d)\n", __func__, ret);
+       } else {
+               pr_info("%s 0x%x == 0x%x, just return\n", __func__, reg_val, ret);
+               return 0;
+       }
+
+       ret = s2mu004_i2c_read_byte(i2c, 0x1e);
+       if (ret < 0)
+               pr_err("%s err read reg 0x1e(%d)\n", __func__, ret);
+       else
+               pr_info("%s after change(0x%x)\n", __func__, ret);
+
+       return ret;
+}
+
+static int s2mu004_muic_init_otg_reg(struct s2mu004_muic_data *muic_data)
+{
+       struct i2c_client *i2c = muic_data->i2c;
+       u8 reg_val;
+       int ret = 0;
+
+       /* 0x73 : check EVT0 or EVT1 */
+       ret = s2mu004_i2c_read_byte(i2c, 0x73);
+       if (ret < 0)
+               pr_err("%s err read 'reg 0x73'(%d)\n", __func__, ret);
+
+       if ((ret & 0xF) > 0)
+               return 0;
+
+       /* 0x89 : hidden register */
+       ret = s2mu004_i2c_read_byte(i2c, 0x89);
+       if (ret < 0)
+               pr_err("%s err read 'reg 0x89'(%d)\n", __func__, ret);
+
+       /* Set 0x89[1] bit : T_DET_VAL */
+       reg_val = ret | (0x1 << 1);
+
+       if (reg_val ^ ret) {
+               pr_info("%s 0x%x != 0x%x, update\n", __func__, reg_val, ret);
+
+               ret = s2mu004_i2c_guaranteed_wbyte(i2c, 0x89, reg_val);
+               if (ret < 0)
+                       pr_err("%s err write(%d)\n", __func__, ret);
+       } else {
+               pr_info("%s 0x%x == 0x%x, just return\n", __func__, reg_val, ret);
+               return 0;
+       }
+
+       ret = s2mu004_i2c_read_byte(i2c, 0x89);
+       if (ret < 0)
+               pr_err("%s err read 'reg 0x89'(%d)\n", __func__, ret);
+       else
+               pr_info("%s after change(0x%x)\n", __func__, ret);
+
+       /* 0x92 : hidden register */
+       ret = s2mu004_i2c_read_byte(i2c, 0x92);
+       if (ret < 0)
+               pr_err("%s err read 'reg 0x92'(%d)\n", __func__, ret);
+
+       /* Set 0x92[7] bit : EN_JIG_AP */
+       reg_val = ret | (0x1 << 7);
+
+       if (reg_val ^ ret) {
+               pr_info("%s 0x%x != 0x%x, update\n", __func__, reg_val, ret);
+
+               ret = s2mu004_i2c_guaranteed_wbyte(i2c, 0x92, reg_val);
+               if (ret < 0)
+                       pr_err("%s err write(%d)\n", __func__, ret);
+       } else {
+               pr_info("%s 0x%x == 0x%x, just return\n",       __func__, reg_val, ret);
+               return 0;
+       }
+
+       ret = s2mu004_i2c_read_byte(i2c, 0x92);
+       if (ret < 0)
+               pr_err("%s err read 'reg 0x92'(%d)\n", __func__, ret);
+       else
+               pr_info("%s after change(0x%x)\n", __func__, ret);
+
+       return ret;
+}
+#endif
+
+/* TODO: There is no needs to use JIGB pin by MUIC if CCIC is supported */
+int s2mu004_muic_jig_on(struct s2mu004_muic_data *muic_data)
+{
+       struct muic_platform_data *muic_pdata = muic_data->pdata;
+       bool en = muic_pdata->is_jig_on;
+       int reg = 0, ret = 0;
+
+       pr_err("%s: %s\n", __func__, en ? "on" : "off");
+
+       reg = s2mu004_i2c_read_byte(muic_data->i2c, S2MU004_REG_MUIC_SW_CTRL);
+
+       if (en)
+               reg |= MANUAL_SW_JIG_EN;
+       else
+               reg &= ~(MANUAL_SW_JIG_EN);
+
+       ret = s2mu004_i2c_write_byte(muic_data->i2c,
+                                    S2MU004_REG_MUIC_SW_CTRL, (u8) reg);
+       if (en) {
+               reg = s2mu004_i2c_read_byte(muic_data->i2c, S2MU004_REG_MUIC_CTRL1);
+
+               return s2mu004_i2c_guaranteed_wbyte(muic_data->i2c,
+                                                   S2MU004_REG_MUIC_CTRL1,
+                                                   (u8) reg);
+       } else
+               return ret;
+
+}
+
+static int s2mu004_muic_set_ctrl_reg(struct s2mu004_muic_data *muic_data, int shift, bool on)
+{
+       struct i2c_client *i2c = muic_data->i2c;
+       u8 reg_val;
+       int ret = 0;
+
+       ret = s2mu004_i2c_read_byte(i2c, S2MU004_REG_MUIC_CTRL1);
+       if (ret < 0)
+               pr_err("%s err read CTRL(%d)\n", __func__, ret);
+
+       if (on)
+               reg_val = ret | (0x1 << shift);
+       else
+               reg_val = ret & ~(0x1 << shift);
+
+       if (reg_val ^ ret) {
+               pr_info("%s 0x%x != 0x%x, update\n", __func__, reg_val, ret);
+
+               ret = s2mu004_i2c_guaranteed_wbyte(i2c, S2MU004_REG_MUIC_CTRL1,
+                                                  reg_val);
+               if (ret < 0)
+                       pr_err("%s err write(%d)\n", __func__, ret);
+       } else {
+               pr_info("%s 0x%x == 0x%x, just return\n",
+                       __func__, reg_val, ret);
+               return 0;
+       }
+
+       ret = s2mu004_i2c_read_byte(i2c, S2MU004_REG_MUIC_CTRL1);
+       if (ret < 0)
+               pr_err("%s err read CTRL(%d)\n", __func__, ret);
+       else
+               pr_info("%s after change(0x%x)\n", __func__, ret);
+
+       return ret;
+}
+
+static int s2mu004_muic_set_int_mask(struct s2mu004_muic_data *muic_data, bool on)
+{
+       int shift = CTRL_INT_MASK_SHIFT;
+       int ret = 0;
+
+       ret = s2mu004_muic_set_ctrl_reg(muic_data, shift, on);
+
+       return ret;
+}
+
+static int s2mu004_muic_set_manual_sw(struct s2mu004_muic_data *muic_data, bool on)
+{
+       int shift = CTRL_MANUAL_SW_SHIFT;
+       int ret = 0;
+
+       ret = s2mu004_muic_set_ctrl_reg(muic_data, shift, on);
+
+       return ret;
+}
+
+static int s2mu004_muic_set_com_sw(struct s2mu004_muic_data *muic_data,
+                                  u8 reg_val)
+{
+       struct i2c_client *i2c = muic_data->i2c;
+       int ret = 0;
+       int temp = 0;
+
+       /*  --- MANSW [7:5][4:2][1][0] : DM DP RSVD JIG  --- */
+       temp = s2mu004_i2c_read_byte(i2c, S2MU004_REG_MUIC_SW_CTRL);
+       if (temp < 0)
+               pr_err("%s err read MANSW(0x%x)\n", __func__, temp);
+
+       if ((reg_val & MANUAL_SW_DM_DP_MASK) != (temp & MANUAL_SW_DM_DP_MASK)) {
+               pr_info("%s 0x%x != 0x%x, update\n", __func__,
+                       (reg_val & MANUAL_SW_DM_DP_MASK), (temp & MANUAL_SW_DM_DP_MASK));
+
+               ret = s2mu004_i2c_guaranteed_wbyte(i2c,
+                       S2MU004_REG_MUIC_SW_CTRL, ((reg_val & MANUAL_SW_DM_DP_MASK)|(temp & 0x03)));
+               if (ret < 0)
+                       pr_err("%s err write MANSW(0x%x)\n", __func__,
+                       (reg_val & MANUAL_SW_DM_DP_MASK) | (temp & 0x03));
+       } else {
+               pr_info("%s MANSW reg(0x%x), just pass\n", __func__, reg_val);
+       }
+
+       return ret;
+}
+
+int s2mu004_muic_com_to_open(struct s2mu004_muic_data *muic_data)
+{
+       int ret = 0;
+
+       pr_info("%s\n", __func__);
+
+       ret = s2mu004_muic_set_com_sw(muic_data, MANSW_OPEN);
+       if (ret)
+               pr_err("%s set_com_sw err\n", __func__);
+
+       return ret;
+}
+
+int s2mu004_muic_com_to_usb(struct s2mu004_muic_data *muic_data)
+{
+       u8 reg_val;
+       int ret = 0;
+
+       reg_val = MANSW_USB;
+       ret = s2mu004_muic_set_com_sw(muic_data, reg_val);
+       if (ret)
+               pr_err("%s set_com_usb err\n", __func__);
+
+       return ret;
+}
+
+int s2mu004_muic_com_to_otg(struct s2mu004_muic_data *muic_data)
+{
+       u8 reg_val;
+       int ret = 0;
+
+       reg_val = MANSW_OTG;
+       ret = s2mu004_muic_set_com_sw(muic_data, reg_val);
+       if (ret)
+               pr_err("%s set_com_otg err\n", __func__);
+
+       return ret;
+}
+
+int s2mu004_muic_com_to_uart(struct s2mu004_muic_data *muic_data)
+{
+       struct muic_platform_data *muic_pdata = muic_data->pdata;
+       u8 reg_val;
+       int ret = 0;
+
+       if (muic_data->pdata->is_rustproof) {
+               pr_info("%s rustproof mode\n", __func__);
+               return ret;
+       }
+
+       if (muic_pdata->uart_path == MUIC_PATH_UART_AP) {
+               reg_val = MANSW_UART_AP;
+#if IS_ENABLED(CONFIG_CP_UART_NOTI)
+               send_uart_noti_to_modem(MODEM_CTRL_UART_AP);
+#endif
+       } else {
+               reg_val = MANSW_UART_CP;
+#if IS_ENABLED(CONFIG_CP_UART_NOTI)
+               send_uart_noti_to_modem(MODEM_CTRL_UART_CP);
+#endif
+       }
+
+       ret = s2mu004_muic_set_com_sw(muic_data, reg_val);
+       if (ret)
+               pr_err("%s set_com_uart err\n", __func__);
+
+       return ret;
+}
+
+int s2mu004_muic_com_to_audio(struct s2mu004_muic_data *muic_data)
+{
+       u8 reg_val;
+       int ret = 0;
+
+       reg_val = MANSW_AUDIO;
+       ret = s2mu004_muic_set_com_sw(muic_data, reg_val);
+       if (ret)
+               pr_err("%s set_com_audio err\n", __func__);
+
+       return ret;
+}
+
+#if IS_ENABLED(CONFIG_MUIC_S2MU004_RID)
+static int s2mu004_i2c_update_bit(struct i2c_client *i2c,
+                                 u8 reg, u8 mask, u8 shift, u8 value)
+{
+       int ret;
+       u8 reg_val = 0;
+
+       reg_val = s2mu004_i2c_read_byte(i2c, reg);
+       reg_val &= ~mask;
+       reg_val |= value << shift;
+       ret = s2mu004_i2c_write_byte(i2c, reg, reg_val);
+       pr_info("%s reg(0x%x) value(0x%x)\n", __func__, reg, reg_val);
+       if (ret < 0)
+               pr_err("%s  Reg = 0x%X, mask = 0x%X, val = 0x%X write err : %d\n",
+                    __func__, reg, mask, value, ret);
+
+       return ret;
+}
+
+static int s2mu004_muic_set_rid_adc_en(struct s2mu004_muic_data *muic_data, bool en)
+{
+       struct i2c_client *i2c = muic_data->i2c;
+       int ret = 0;
+
+       pr_info("%s rid en : (%d)\n", __func__, en);
+       if (en) {
+               /* enable rid detection for muic dp dm detect */
+               ret = s2mu004_i2c_update_bit(i2c,
+                                            S2MU004_REG_MUIC_RID_CTRL,
+                                            RID_CTRL_ADC_OFF_MASK,
+                                            RID_CTRL_ADC_OFF_SHIFT, 0x0);
+       } else {
+               /* disable rid detection for muic dp dm detect */
+               ret = s2mu004_i2c_update_bit(i2c,
+                                            S2MU004_REG_MUIC_RID_CTRL,
+                                            RID_CTRL_ADC_OFF_MASK,
+                                            RID_CTRL_ADC_OFF_SHIFT, 0x1);
+       }
+
+       return ret;
+}
+
+int s2mu004_muic_recheck_adc(struct s2mu004_muic_data *muic_data)
+{
+       s2mu004_muic_set_rid_adc_en(muic_data, false);
+       usleep_range(10000, 12000);
+       s2mu004_muic_set_rid_adc_en(muic_data, true);
+       msleep(RID_REFRESH_DURATION_MS);
+       return s2mu004_i2c_read_byte(muic_data->i2c, S2MU004_REG_MUIC_ADC) & ADC_MASK;
+}
+
+int s2mu004_muic_refresh_adc(struct s2mu004_muic_data *muic_data)
+{
+       struct i2c_client *i2c = muic_data->i2c;
+       int adc = 0;
+       u8 reg_data, b_Rid_en = 0;
+
+       reg_data = s2mu004_i2c_read_byte(i2c, S2MU004_REG_MUIC_RID_CTRL);
+       if (!(reg_data & 0x2)) {
+               b_Rid_en = 1;
+       } else {
+               pr_info("%s, enable the RID\n", __func__);
+               reg_data &= ~(0x01 << 1);
+               s2mu004_i2c_write_byte(i2c, S2MU004_REG_MUIC_RID_CTRL, reg_data);
+               msleep(35);
+       }
+
+       adc = s2mu004_i2c_read_byte(i2c, S2MU004_REG_MUIC_ADC);
+       pr_info("%s, adc : 0x%X\n", __func__, adc);
+
+       if (!b_Rid_en) {
+               pr_info("%s, disable the RID\n", __func__);
+               reg_data |= (0x01 << 1);
+               s2mu004_i2c_write_byte(i2c, S2MU004_REG_MUIC_RID_CTRL, reg_data);
+       }
+       return adc;
+}
+#endif
+
+int s2mu004_muic_get_vbus_state(struct s2mu004_muic_data *muic_data)
+{
+       struct i2c_client *i2c = muic_data->i2c;
+       u8 reg_val = 0;
+       int vbus = 0;
+
+       reg_val = s2mu004_i2c_read_byte(i2c, S2MU004_REG_MUIC_DEVICE_APPLE);
+       vbus = !!(reg_val & DEV_TYPE_APPLE_VBUS_WAKEUP);
+       pr_info("%s vbus : (%d)\n", __func__, vbus);
+
+       return vbus;
+}
+
+static void s2mu004_muic_get_detect_info(struct s2mu004_muic_data *muic_data)
+{
+       struct muic_platform_data *muic_pdata = muic_data->pdata;
+       struct i2c_client *i2c = muic_data->i2c;
+
+       muic_data->reg[DEVICE_TYPE1] = s2mu004_i2c_read_byte(i2c, S2MU004_REG_MUIC_DEVICE_TYPE1);
+       muic_data->reg[DEVICE_TYPE2] = s2mu004_i2c_read_byte(i2c, S2MU004_REG_MUIC_DEVICE_TYPE2);
+       muic_data->reg[DEVICE_TYPE3] = s2mu004_i2c_read_byte(i2c, S2MU004_REG_MUIC_DEVICE_TYPE3);
+       muic_data->reg[REV_ID] = s2mu004_i2c_read_byte(i2c, S2MU004_REG_REV_ID);
+       muic_data->reg[ADC] = s2mu004_i2c_read_byte(i2c, S2MU004_REG_MUIC_ADC);
+       muic_data->reg[DEVICE_APPLE] = s2mu004_i2c_read_byte(i2c, S2MU004_REG_MUIC_DEVICE_APPLE);
+       muic_data->reg[CHG_TYPE] = s2mu004_i2c_read_byte(i2c, S2MU004_REG_MUIC_CHG_TYPE);
+       muic_data->reg[SC_STATUS2] = s2mu004_i2c_read_byte(i2c, S2MU004_REG_SC_STATUS2);
+
+       muic_data->adc = muic_pdata->adc = muic_data->reg[ADC];
+       muic_data->vbvolt = muic_pdata->vbvolt =
+                       !!(muic_data->reg[DEVICE_APPLE] & DEV_TYPE_APPLE_VBUS_WAKEUP);
+       muic_data->vmid = muic_pdata->vmid = !!(muic_data->reg[SC_STATUS2] & 0x7);
+
+       pr_info("dev[1:0x%02x, 2:0x%02x, 3:0x%02x]\n", muic_data->reg[DEVICE_TYPE1],
+               muic_data->reg[DEVICE_TYPE2], muic_data->reg[DEVICE_TYPE3]);
+       pr_info("adc:0x%02x, vbvolt:0x%02x, apple:0x%02x\n",
+               muic_data->adc, muic_data->vbvolt, muic_data->reg[DEVICE_APPLE]);
+       pr_info("chg_type:0x%02x, vmid:0x%02x, dev_id:0x%02x\n",
+               muic_data->reg[CHG_TYPE], muic_data->vmid, muic_data->reg[REV_ID]);
+}
+
+static int s2mu004_muic_reg_init(struct s2mu004_muic_data *muic_data)
+{
+       struct i2c_client *i2c = muic_data->i2c;
+       int ret = 0;
+       struct muic_interface_t *muic_if =
+           (struct muic_interface_t *)muic_data->if_data;
+       int data = 0;
+
+       pr_info("%s\n", __func__);
+
+       s2mu004_muic_get_detect_info(muic_data);
+
+       if (muic_if->opmode == OPMODE_DEVICE) {
+               s2mu004_muic_control_rid_adc(muic_data, S2MU004_DISABLE);
+
+               /* adc, RID int masking */
+               data = s2mu004_i2c_read_byte(i2c, S2MU004_REG_MUIC_INT1_MASK);
+               data |= (INTm_ATTACH_MASK | INTm_KP_MASK);
+               s2mu004_i2c_write_byte(i2c, S2MU004_REG_MUIC_INT1_MASK, data);
+
+               data = s2mu004_i2c_read_byte(i2c, S2MU004_REG_MUIC_INT2_MASK);
+               data |= INTm_ADC_CHANGE_MASK;
+               s2mu004_i2c_write_byte(i2c, S2MU004_REG_MUIC_INT2_MASK, data);
+       }
+
+       s2mu004_muic_control_vbus_det(muic_data, false);
+       ret = s2mu004_i2c_guaranteed_wbyte(i2c,
+               S2MU004_REG_MUIC_CTRL1, CTRL_MASK);
+       if (ret < 0)
+               pr_err("failed to write ctrl(%d)\n", ret);
+
+       /*
+        * These registers represents the RID ADC LDO voltage control.
+        * Low / High LDO initialized to 3V, 2.7V each.
+        */
+
+#ifdef CONFIG_MUIC_S2MU004_RID
+       s2mu004_muic_control_rid_adc(muic_data, S2MU004_ENABLE);
+
+       s2mu004_i2c_update_bit(i2c,
+                       S2MU004_REG_LDOADC_VSETL, LDOADC_VSETH_MASK, 0, LDOADC_VSET_3V);
+       s2mu004_i2c_update_bit(i2c,
+                       S2MU004_REG_LDOADC_VSETH, LDOADC_VSETH_MASK, 0, LDOADC_VSET_2_7V);
+
+       s2mu004_i2c_update_bit(i2c,
+                              S2MU004_REG_LDOADC_VSETH,
+                              LDOADC_VSETH_WAKE_HYS_MASK,
+                              LDOADC_VSETH_WAKE_HYS_SHIFT, 0x1);
+#endif
+
+#if defined(CONFIG_VBUS_NOTIFIER)
+       vbus_notifier_handle((!!muic_data->vbvolt) ? STATUS_VBUS_HIGH : STATUS_VBUS_LOW);
+#endif /* CONFIG_VBUS_NOTIFIER */
+
+       return ret;
+}
+
+static void s2mu004_muic_detect_dev_ccic(struct s2mu004_muic_data *muic_data,
+       muic_attached_dev_t new_dev)
+{
+       struct muic_platform_data *muic_pdata = muic_data->pdata;
+       int adc = 0, vbvolt = 0;
+
+       pr_info("%s (new_dev=%s)\n", __func__, dev_to_str(new_dev));
+
+       if (muic_pdata->attached_dev == new_dev) {
+               pr_info("Skip to handle duplicated type\n");
+               return;
+       }
+
+       if (new_dev == ATTACHED_DEV_NONE_MUIC) {
+               /* Detach from CCIC */
+               if (muic_core_get_ccic_cable_state(muic_data->pdata) == false) {
+                       pr_info("Skip to detach legacy type\n");
+                       return;
+               }
+               if (muic_pdata->attached_dev == ATTACHED_DEV_OTG_MUIC) {
+                       /* disable vbus det for interrupt */
+                       s2mu004_muic_control_vbus_det(muic_data, false);
+               }
+               muic_pdata->attached_dev = ATTACHED_DEV_NONE_MUIC;
+       } else {
+               /* Attach from CCIC */
+               muic_pdata->attached_dev = new_dev;
+               pr_info("%s DETECTED\n", dev_to_str(new_dev));
+
+               switch (new_dev) {
+               case ATTACHED_DEV_OTG_MUIC:
+                       s2mu004_muic_set_com_sw(muic_data, MANSW_USB);
+
+                       /* enable vbus det for interrupt */
+                       s2mu004_muic_control_vbus_det(muic_data, true);
+                       break;
+               case ATTACHED_DEV_TYPE3_CHARGER_MUIC: /* PD Charger */
+                       s2mu004_muic_set_com_sw(muic_data, MANSW_OPEN);
+                       return;
+               default:
+                       break;
+               }
+       }
+
+       if (muic_pdata->attached_dev != ATTACHED_DEV_NONE_MUIC) {
+               adc = s2mu004_i2c_read_byte(muic_data->i2c, S2MU004_REG_MUIC_ADC);
+               vbvolt = s2mu004_i2c_read_byte(muic_data->i2c, S2MU004_REG_MUIC_DEVICE_APPLE);
+               muic_core_handle_attach(muic_data->pdata, new_dev, adc, !!vbvolt);
+       } else if (muic_pdata->attached_dev == ATTACHED_DEV_NONE_MUIC) {
+               muic_core_handle_detach(muic_data->pdata);
+       }
+}
+
+static int s2mu004_muic_detect_dev_bc1p2(struct s2mu004_muic_data *muic_data)
+{
+       struct muic_interface_t *muic_if = (struct muic_interface_t *)muic_data->if_data;
+
+       muic_data->new_dev = ATTACHED_DEV_UNKNOWN_MUIC;
+
+       /* Attached */
+       switch (muic_data->reg[DEVICE_TYPE1]) {
+       case DEV_TYPE1_CDP:
+               if (muic_data->vbvolt) {
+                       muic_data->new_dev = ATTACHED_DEV_CDP_MUIC;
+                       pr_info("USB_CDP DETECTED\n");
+               }
+               break;
+       case DEV_TYPE1_USB:
+               if (muic_data->vbvolt) {
+                       muic_data->new_dev = ATTACHED_DEV_USB_MUIC;
+                       pr_info("USB DETECTED\n");
+               }
+               break;
+       case DEV_TYPE1_DEDICATED_CHG:
+       case 0x44:
+       case 0x60:
+               if (muic_data->vbvolt) {
+                       muic_if->is_dcp_charger = true;
+                       muic_data->new_dev = ATTACHED_DEV_TA_MUIC;
+                       muic_data->afc_check = true;
+                       pr_info("DEDICATED CHARGER DETECTED\n");
+               }
+               break;
+#ifdef CONFIG_MUIC_S2MU004_RID
+       case DEV_TYPE1_USB_OTG:
+               if (muic_if->opmode == OPMODE_DEVICE) {
+                       muic_data->new_dev = ATTACHED_DEV_OTG_MUIC;
+                       pr_info("USB_OTG DETECTED\n");
+               }
+               break;
+#endif /* CONFIG_MUIC_S2MU004_RID */
+       case DEV_TYPE1_T1_T2_CHG:
+               if (muic_data->vbvolt) {
+                       /* 200K, 442K should be checkef */
+                       if (muic_data->adc == ADC_CEA936ATYPE2_CHG) {
+                               muic_data->new_dev = ATTACHED_DEV_JIG_UART_OFF_VB_MUIC;
+                               pr_info("CEA936ATYPE2_CHG DETECTED\n");
+                               muic_data->afc_check = false;
+                       } else {
+                               muic_data->new_dev = ATTACHED_DEV_USB_MUIC;
+                               pr_info("T1_T2_CHG DETECTED\n");
+                       }
+               }
+               break;
+       default:
+               break;
+       }
+
+       if (muic_data->new_dev != ATTACHED_DEV_UNKNOWN_MUIC &&
+                       muic_data->new_dev != ATTACHED_DEV_NONE_MUIC)
+               goto detect_done;
+
+       switch (muic_data->reg[DEVICE_TYPE2]) {
+       case DEV_TYPE2_SDP_1P8S:
+               if (muic_data->vbvolt) {
+                       muic_data->new_dev = ATTACHED_DEV_TIMEOUT_OPEN_MUIC;
+                       pr_info("%s:%s: SDP_1P8S DETECTED\n", MUIC_DEV_NAME, __func__);
+                       muic_if->is_dcdtmr_intr = true;
+                       schedule_delayed_work(&muic_data->dcd_recheck, 0);
+               }
+               break;
+       default:
+               break;
+       }
+
+       if (muic_data->new_dev != ATTACHED_DEV_UNKNOWN_MUIC &&
+                       muic_data->new_dev != ATTACHED_DEV_NONE_MUIC)
+               goto detect_done;
+
+       if (muic_data->vbvolt && ((muic_data->reg[DEVICE_APPLE] & DEV_TYPE_APPLE_APPLE2P4A_CHG)
+               || (muic_data->reg[DEVICE_APPLE] & DEV_TYPE_APPLE_APPLE2A_CHG)
+               || (muic_data->reg[DEVICE_APPLE] & DEV_TYPE_APPLE_APPLE1A_CHG)
+               || (muic_data->reg[DEVICE_APPLE] & DEV_TYPE_APPLE_APPLE0P5A_CHG))) {
+               muic_data->new_dev = ATTACHED_DEV_TA_MUIC;
+               muic_data->afc_check = false;
+               pr_info("APPLE_CHG DETECTED\n");
+               muic_if->is_dcdtmr_intr = true;
+               schedule_delayed_work(&muic_data->dcd_recheck, 0);
+       }
+
+       if ((muic_data->reg[CHG_TYPE] & DEV_TYPE_CHG_TYPE) &&
+                       (muic_data->new_dev == ATTACHED_DEV_UNKNOWN_MUIC)) {
+               muic_data->new_dev = ATTACHED_DEV_TA_MUIC;
+               muic_data->afc_check = false;
+               pr_info("CHG_TYPE DETECTED\n");
+               muic_if->is_dcdtmr_intr = true;
+               schedule_delayed_work(&muic_data->dcd_recheck, 0);
+       }
+
+detect_done:
+       if (muic_data->new_dev != ATTACHED_DEV_UNKNOWN_MUIC)
+               return S2MU004_DETECT_DONE;
+       else
+               return S2MU004_DETECT_NONE;
+}
+
+#if IS_ENABLED(CONFIG_MUIC_S2MU004_RID)
+static int s2mu004_muic_detect_dev_jig_type(struct s2mu004_muic_data *muic_data)
+{
+       switch (muic_data->reg[DEVICE_TYPE2]) {
+       case DEV_TYPE2_JIG_UART_OFF:
+               if (muic_data->vbvolt)
+                       muic_data->new_dev = ATTACHED_DEV_JIG_UART_OFF_VB_MUIC;
+               else
+                       muic_data->new_dev = ATTACHED_DEV_JIG_UART_OFF_MUIC;
+               pr_info("JIG_UART_OFF DETECTED\n");
+               break;
+       case DEV_TYPE2_JIG_USB_OFF:
+               if (!muic_data->vbvolt)
+                       break;
+               muic_data->new_dev = ATTACHED_DEV_JIG_USB_OFF_MUIC;
+               pr_info("JIG_USB_OFF DETECTED\n");
+               break;
+       case DEV_TYPE2_JIG_USB_ON:
+               if (!muic_data->vbvolt)
+                       break;
+               muic_data->new_dev = ATTACHED_DEV_JIG_USB_ON_MUIC;
+               pr_info("JIG_USB_ON DETECTED\n");
+               break;
+       case DEV_TYPE2_JIG_UART_ON:
+               if (muic_data->new_dev != ATTACHED_DEV_JIG_UART_ON_MUIC) {
+                       if (!muic_data->vbvolt) {
+                               muic_data->new_dev = ATTACHED_DEV_JIG_UART_ON_MUIC;
+                               pr_info("ADC JIG_UART_ON DETECTED\n");
+                       } else {
+                               muic_data->new_dev = ATTACHED_DEV_JIG_UART_ON_VB_MUIC;
+                               pr_info("ADC JIG_UART_ON_VB DETECTED\n");
+                       }
+               }
+               break;
+       default:
+               break;
+       }
+
+       return muic_data->new_dev;
+}
+
+static int s2mu004_muic_detect_dev_rid_array(struct s2mu004_muic_data *muic_data)
+{
+       switch (muic_data->adc) {
+       case ADC_JIG_UART_OFF:
+               pr_info("ADC_JIG_UART_OFF\n");
+               if (muic_data->vbvolt)
+                       muic_data->new_dev = ATTACHED_DEV_JIG_UART_OFF_VB_MUIC;
+               else
+                       muic_data->new_dev = ATTACHED_DEV_JIG_UART_OFF_MUIC;
+               break;
+       case ADC_JIG_USB_ON:
+       case ADC_JIG_USB_OFF:
+       case ADC_DESKDOCK:
+               muic_data->new_dev = ATTACHED_DEV_JIG_USB_ON_MUIC;
+               pr_info("ADC JIG_USB_ON DETECTED\n");
+               break;
+#if defined(CONFIG_SEC_FACTORY)
+       case ADC_CEA936ATYPE1_CHG:
+               if (muic_data->vbvolt) {
+                       muic_data->new_dev = ATTACHED_DEV_CARKIT_MUIC;
+                       pr_info("SMD DL 255k 200k charger disable\n");
+               } else {
+                       return;
+               }
+               break;
+#endif
+       default:
+               pr_info("%s unsupported ADC(0x%02x)\n", __func__, muic_data->adc);
+       }
+
+       return muic_data->new_dev;
+}
+
+static int s2mu004_muic_detect_dev_rid_device(struct s2mu004_muic_data *muic_data)
+{
+       struct muic_platform_data *muic_pdata = muic_data->pdata;
+
+       switch (muic_data->adc) {
+       case ADC_CEA936ATYPE1_CHG:      /*200k ohm */
+               if (muic_data->vbvolt) {
+                       /* This is workaournd for LG USB cable which has 219k ohm ID */
+                       muic_data->new_dev = ATTACHED_DEV_USB_MUIC;
+                       pr_info("TYPE1 CHARGER DETECTED(USB)\n");
+               }
+               break;
+       case ADC_CEA936ATYPE2_CHG:
+               if (muic_data->vbvolt) {
+                       muic_data->new_dev = ATTACHED_DEV_UNDEFINED_RANGE_MUIC;
+                       muic_data->afc_check = false;
+                       pr_info("%s unsupported ADC(0x%02x)\n", __func__, muic_data->adc);
+               }
+               break;
+       case ADC_JIG_USB_OFF:   /* 255k */
+               if (!muic_data->vbvolt)
+                       break;
+               if (muic_data->new_dev != ATTACHED_DEV_JIG_USB_OFF_MUIC) {
+                       muic_data->new_dev = ATTACHED_DEV_JIG_USB_OFF_MUIC;
+                       pr_info("ADC JIG_USB_OFF DETECTED\n");
+               }
+               break;
+       case ADC_JIG_USB_ON:
+               if (!muic_data->vbvolt)
+                       break;
+               if (muic_data->new_dev != ATTACHED_DEV_JIG_USB_ON_MUIC) {
+                       if (!muic_data->vbvolt) {
+                               muic_data->new_dev = ATTACHED_DEV_JIG_UART_ON_MUIC;
+                               pr_info("ADC JIG_UART_ON DETECTED\n");
+                       } else {
+                               muic_data->new_dev = ATTACHED_DEV_JIG_UART_ON_VB_MUIC;
+                               pr_info("ADC JIG_UART_ON_VB DETECTED\n");
+                       }
+               }
+               break;
+       case ADC_JIG_UART_OFF:
+               if (muic_pdata->is_otg_test) {
+                       mdelay(100);
+                       if (muic_data->vmid == 0x4) {
+                               pr_info("OTG_TEST DETECTED, vmid = %d\n", muic_data->vmid);
+                               muic_data->vbvolt = 1;
+                               muic_data->new_dev = ATTACHED_DEV_JIG_UART_OFF_VB_OTG_MUIC;
+                       } else
+                               muic_data->new_dev = ATTACHED_DEV_JIG_UART_OFF_MUIC;
+               } else if (muic_data->vbvolt)
+                       muic_data->new_dev = ATTACHED_DEV_JIG_UART_OFF_VB_MUIC;
+               else
+                       muic_data->new_dev = ATTACHED_DEV_JIG_UART_OFF_MUIC;
+
+               pr_info("ADC JIG_UART_OFF DETECTED\n");
+               break;
+       case ADC_JIG_UART_ON:
+               if (muic_data->new_dev != ATTACHED_DEV_JIG_UART_ON_MUIC) {
+                       muic_data->new_dev = ATTACHED_DEV_JIG_UART_ON_MUIC;
+                       pr_info("ADC JIG_UART_ON DETECTED\n");
+               }
+               break;
+       case ADC_SMARTDOCK: /* 0x10000 40.2K ohm */
+               /* SMARTDOCK is not supported */
+               /* force not to charge the device with SMARTDOCK */
+               muic_data->new_dev = ATTACHED_DEV_UNSUPPORTED_ID_VB_MUIC;
+               pr_info("%s unsupported ADC(0x%02x) but charging\n",
+                       __func__, muic_data->adc);
+               break;
+       case ADC_HMT:   /* 0x10001 49.9K ohm */
+               muic_data->new_dev = ATTACHED_DEV_UNKNOWN_MUIC;
+               pr_info("%s unsupported ADC(0x%02x) not charging\n",
+                       __func__, muic_data->adc);
+               break;
+       case ADC_AUDIODOCK:
+#ifdef CONFIG_MUIC_S2MU004_SUPPORT_AUDIODOCK
+               muic_data->new_dev = ATTACHED_DEV_AUDIODOCK_MUIC;
+#else
+               muic_data->new_dev = ATTACHED_DEV_UNDEFINED_RANGE_MUIC;
+#endif
+               pr_info("ADC AUDIODOCK DETECTED\n");
+               break;
+       case ADC_UNIVERSAL_MMDOCK:
+               muic_data->new_dev = ATTACHED_DEV_UNKNOWN_MUIC;
+               pr_info("%s unsupported ADC(0x%02x) not charging\n",
+                       __func__, muic_data->adc);
+               break;
+       case ADC_OPEN:
+               /* sometimes muic fails to catch JIG_UART_OFF detaching */
+               /* double check with ADC */
+               if (muic_data->new_dev == ATTACHED_DEV_JIG_UART_OFF_MUIC) {
+                       muic_data->new_dev = ATTACHED_DEV_UNKNOWN_MUIC;
+                       pr_info("ADC OPEN DETECTED\n");
+               }
+               break;
+       default:
+               muic_data->new_dev = ATTACHED_DEV_UNDEFINED_RANGE_MUIC;
+               pr_info("%s unsupported ADC(0x%02x)\n", __func__, muic_data->adc);
+       }
+
+       return muic_data->new_dev;
+}
+
+static int s2mu004_muic_detect_dev_mrid_adc(struct s2mu004_muic_data *muic_data)
+{
+       struct muic_interface_t *muic_if = (struct muic_interface_t *)muic_data->if_data;
+
+       muic_data->new_dev = ATTACHED_DEV_UNKNOWN_MUIC;
+
+       if (muic_data->adc & ADC_CONVERSION_ERR_MASK) {
+               pr_info("%s ADC conversion error! (adc=%#x)\n", __func__, muic_data->adc);
+               return muic_data->new_dev;
+       }
+
+       if (muic_if->opmode == OPMODE_SMD_ARRAY)
+               muic_data->new_dev = s2mu004_muic_detect_dev_rid_array(muic_data);
+       else if (muic_if->opmode == OPMODE_DEVICE)
+               muic_data->new_dev = s2mu004_muic_detect_dev_rid_device(muic_data);
+
+       s2mu004_muic_detect_dev_jig_type(muic_data);
+
+       if (muic_data->new_dev != ATTACHED_DEV_UNKNOWN_MUIC)
+               return S2MU004_DETECT_DONE;
+       else
+               return S2MU004_DETECT_NONE;
+}
+#endif /* CONFIG_MUIC_S2MU004_RID */
+
+static irqreturn_t s2mu004_muic_attach_isr(int irq, void *data)
+{
+       struct s2mu004_muic_data *muic_data = data;
+       struct muic_platform_data *muic_pdata = muic_data->pdata;
+       int ret = 0;
+
+       if (muic_data == NULL || muic_pdata == NULL) {
+               pr_err("%s, data NULL\n", __func__);
+               return IRQ_NONE;
+       }
+
+       pr_info("%s start\n", __func__);
+
+       if (muic_core_is_cable_attached(muic_pdata)) {
+               pr_info("%s Cable type already detected\n", __func__);
+               goto irq_skip;
+       }
+
+       mutex_lock(&muic_data->muic_mutex);
+       wake_lock(&muic_data->wake_lock);
+
+       s2mu004_muic_get_detect_info(muic_data);
+
+       if (muic_pdata->vbvolt) {
+               ret = s2mu004_muic_detect_dev_bc1p2(muic_data);
+               if (ret == S2MU004_DETECT_DONE)
+                       goto attach_handle;
+       }
+
+#if IS_ENABLED(CONFIG_MUIC_S2MU004_RID)
+       ret = s2mu004_muic_detect_dev_mrid_adc(muic_data);
+#endif
+
+attach_handle:
+       if (muic_data->new_dev != ATTACHED_DEV_NONE_MUIC &&
+                               muic_data->new_dev != muic_pdata->attached_dev) {
+               muic_manager_set_legacy_dev(muic_pdata->muic_if, muic_data->new_dev);
+               muic_core_handle_attach(muic_pdata, muic_data->new_dev,
+                               muic_pdata->adc, muic_pdata->vbvolt);
+       }
+
+       wake_unlock(&muic_data->wake_lock);
+       mutex_unlock(&muic_data->muic_mutex);
+
+irq_skip:
+       pr_info("%s done(%s)\n", __func__, dev_to_str(muic_pdata->attached_dev));
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t s2mu004_muic_detach_isr(int irq, void *data)
+{
+       struct s2mu004_muic_data *muic_data = data;
+       struct muic_platform_data *muic_pdata = muic_data->pdata;
+       struct muic_interface_t *muic_if = muic_data->if_data;
+
+       if (muic_data == NULL || muic_pdata == NULL || muic_if == NULL) {
+               pr_err("%s, data NULL\n", __func__);
+               return IRQ_NONE;
+       }
+
+       pr_info("%s start\n", __func__);
+
+       if (muic_core_is_cable_attached(muic_pdata) == false) {
+               pr_info("%s Cable type already detached\n", __func__);
+               goto irq_skip;
+       }
+
+       mutex_lock(&muic_data->muic_mutex);
+       wake_lock(&muic_data->wake_lock);
+
+       if (muic_if->opmode == OPMODE_DEVICE) {
+               if (muic_core_get_ccic_cable_state(muic_data->pdata))
+                       pr_info("%s skip to handle ccic dev\n", __func__);
+               else
+                       muic_core_handle_detach(muic_data->pdata);
+       } else if (muic_if->opmode == OPMODE_SMD_ARRAY)
+               muic_core_handle_detach(muic_data->pdata);
+
+       wake_unlock(&muic_data->wake_lock);
+       mutex_unlock(&muic_data->muic_mutex);
+
+irq_skip:
+       pr_info("%s done(%s)\n", __func__, dev_to_str(muic_pdata->attached_dev));
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t s2mu004_muic_vbus_isr(int irq, void *data)
+{
+       struct s2mu004_muic_data *muic_data = data;
+       struct muic_platform_data *muic_pdata = muic_data->pdata;
+
+       if (muic_data == NULL || muic_pdata == NULL) {
+               pr_err("%s, data NULL\n", __func__);
+               return IRQ_NONE;
+       }
+
+       pr_info("%s start\n", __func__);
+
+       mutex_lock(&muic_data->muic_mutex);
+       wake_lock(&muic_data->wake_lock);
+
+       muic_pdata->vbvolt = muic_data->vbvolt = s2mu004_muic_get_vbus_state(muic_data);
+
+#if defined(CONFIG_VBUS_NOTIFIER)
+       vbus_notifier_handle(muic_data->vbvolt ? STATUS_VBUS_HIGH : STATUS_VBUS_LOW);
+#endif /* CONFIG_VBUS_NOTIFIER */
+
+       wake_unlock(&muic_data->wake_lock);
+       mutex_unlock(&muic_data->muic_mutex);
+
+       pr_info("%s done(%s)\n", __func__, dev_to_str(muic_pdata->attached_dev));
+
+       return IRQ_HANDLED;
+}
+
+#if IS_ENABLED(CONFIG_MUIC_S2MU004_RID)
+static irqreturn_t s2mu004_muic_rid_chg_isr(int irq, void *data)
+{
+       struct s2mu004_muic_data *muic_data = data;
+       struct muic_platform_data *muic_pdata = muic_data->pdata;
+
+       pr_info("%s start\n", __func__);
+
+       pr_info("%s done(%s)\n", __func__, dev_to_str(muic_pdata->attached_dev));
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t s2mu004_muic_adc_change_isr(int irq, void *data)
+{
+       struct s2mu004_muic_data *muic_data = data;
+       struct muic_platform_data *muic_pdata = muic_data->pdata;
+
+       pr_info("%s start\n", __func__);
+
+       pr_info("%s done(%s)\n", __func__, dev_to_str(muic_pdata->attached_dev));
+
+       return IRQ_HANDLED;
+
+}
+#endif
+
+static irqreturn_t s2mu004_muic_reserved_isr(int irq, void *data)
+{
+       struct s2mu004_muic_data *muic_data = data;
+       struct muic_platform_data *muic_pdata = muic_data->pdata;
+
+       pr_info("%s start\n", __func__);
+
+       pr_info("%s done(%s)\n", __func__, dev_to_str(muic_pdata->attached_dev));
+
+       return IRQ_HANDLED;
+
+}
+
+static void s2mu004_muic_dcd_recheck(struct work_struct *work)
+{
+       struct s2mu004_muic_data *muic_data =
+           container_of(work, struct s2mu004_muic_data, dcd_recheck.work);
+       struct muic_interface_t *muic_if = muic_data->if_data;
+
+       pr_info("%s\n", __func__);
+       muic_manager_dcd_rescan(muic_if);
+}
+
+void s2mu004_muic_dcd_rescan(struct s2mu004_muic_data *muic_data)
+{
+       int ret = 0;
+       int reg_val = 0;
+       struct i2c_client *i2c = muic_data->i2c;
+
+       mutex_lock(&muic_data->switch_mutex);
+       pr_info("%s call\n", __func__);
+
+       /* muic mux switch open */
+       reg_val = s2mu004_i2c_read_byte(i2c, S2MU004_REG_MUIC_SW_CTRL);
+       ret = s2mu004_muic_com_to_open(muic_data);
+       if (ret < 0)
+               pr_err("%s, fail to open mansw\n", __func__);
+
+#if IS_ENABLED(CONFIG_MUIC_S2MU004_RID)
+       s2mu004_muic_control_rid_adc(muic_data, S2MU004_ENABLE);
+       msleep(50);
+       s2mu004_muic_control_rid_adc(muic_data, S2MU004_DISABLE);
+#endif /* CONFIG_MUIC_S2MU004_RID */
+       msleep(100);
+
+       s2mu004_muic_bcd_rescan(muic_data);
+#if !IS_ENABLED(CONFIG_SEC_FACTORY)
+       msleep(650);
+#else
+       msleep(50);
+#endif
+
+       /* restore muic mux switch */
+       ret = s2mu004_muic_set_com_sw(muic_data, reg_val);
+       if (ret < 0)
+               pr_err("%s, fail to restore mansw\n", __func__);
+
+       mutex_unlock(&muic_data->switch_mutex);
+}
+
+void s2mu004_muic_set_auto(void)
+{
+       s2mu004_i2c_write_byte(static_data->i2c, S2MU004_REG_MUIC_CTRL1, 0xDF);
+}
+EXPORT_SYMBOL(s2mu004_muic_set_auto);
+
+static int s2mu004_init_rev_info(struct s2mu004_muic_data *muic_data)
+{
+       u8 dev_id;
+       int ret = 0;
+
+       dev_id = s2mu004_i2c_read_byte(muic_data->i2c, S2MU004_REG_REV_ID);
+       if (dev_id < 0) {
+               pr_err("%s(%d)\n", __func__, dev_id);
+               ret = -ENODEV;
+       } else {
+               muic_data->muic_vendor = 0x05;
+               muic_data->muic_version = (dev_id & 0x0F);
+               muic_data->ic_rev_id = (dev_id & 0xF0) >> 4;
+               pr_info("%s : vendor=0x%x, ver=0x%x, dev_id=0x%x\n rev id =0x%x\n",
+                    __func__, muic_data->muic_vendor, muic_data->muic_version,
+                    dev_id, muic_data->ic_rev_id);
+       }
+       return ret;
+}
+
+static int s2mu004_muic_irq_init(struct s2mu004_muic_data *muic_data)
+{
+       int ret = 0;
+
+       if (muic_data->mfd_pdata && (muic_data->mfd_pdata->irq_base > 0)) {
+               int irq_base = muic_data->mfd_pdata->irq_base;
+
+               /* request MUIC IRQ */
+               muic_data->irq_attach = irq_base + S2MU004_MUIC_IRQ1_ATTATCH;
+               REQUEST_IRQ(muic_data->irq_attach, muic_data,
+                       "muic-attach", &s2mu004_muic_attach_isr);
+
+               muic_data->irq_detach = irq_base + S2MU004_MUIC_IRQ1_DETACH;
+               REQUEST_IRQ(muic_data->irq_detach, muic_data,
+                       "muic-detach", &s2mu004_muic_detach_isr);
+
+#if IS_ENABLED(CONFIG_MUIC_S2MU004_RID)
+               muic_data->irq_rid_chg = irq_base + S2MU004_MUIC_IRQ1_RID_CHG;
+               REQUEST_IRQ(muic_data->irq_rid_chg, muic_data,
+                       "muic-rid_chg", &s2mu004_muic_rid_chg_isr);
+#endif
+
+               muic_data->irq_vbus_on = irq_base + S2MU004_MUIC_IRQ2_VBUS_ON;
+               REQUEST_IRQ(muic_data->irq_vbus_on, muic_data,
+                       "muic-vbus_on", &s2mu004_muic_vbus_isr);
+
+               muic_data->irq_rsvd_attach = irq_base + S2MU004_MUIC_IRQ2_RSVD_ATTACH;
+               REQUEST_IRQ(muic_data->irq_rsvd_attach, muic_data,
+                       "muic-rsvd_attach", &s2mu004_muic_reserved_isr);
+
+#if IS_ENABLED(CONFIG_MUIC_S2MU004_RID)
+               muic_data->irq_adc_change = irq_base + S2MU004_MUIC_IRQ2_ADC_CHANGE;
+               REQUEST_IRQ(muic_data->irq_adc_change, muic_data,
+                       "muic-adc_change", &s2mu004_muic_adc_change_isr);
+#endif
+
+               muic_data->irq_av_charge = irq_base + S2MU004_MUIC_IRQ2_AV_CHARGE;
+               REQUEST_IRQ(muic_data->irq_av_charge, muic_data,
+                       "muic-av_charge", &s2mu004_muic_reserved_isr);
+
+               muic_data->irq_vbus_off = irq_base + S2MU004_MUIC_IRQ2_VBUS_OFF;
+               REQUEST_IRQ(muic_data->irq_vbus_off, muic_data,
+                       "muic-vbus_off", &s2mu004_muic_vbus_isr);
+
+       }
+
+       pr_info("%s muic-attach(%d), muic-detach(%d), muic-rid_chg(%d), muic-vbus_on(%d)",
+               __func__, muic_data->irq_attach, muic_data->irq_detach, muic_data->irq_rid_chg,
+                       muic_data->irq_vbus_on);
+       pr_info("muic-rsvd_attach(%d), muic-adc_change(%d), muic-av_charge(%d), muic-vbus_off(%d)\n",
+               muic_data->irq_rsvd_attach, muic_data->irq_adc_change, 
+               muic_data->irq_av_charge, muic_data->irq_vbus_off);
+
+       return ret;
+}
+
+static void s2mu004_muic_free_irqs(struct s2mu004_muic_data *muic_data)
+{
+       pr_info("%s\n", __func__);
+
+       /* free MUIC IRQ */
+       FREE_IRQ(muic_data->irq_attach, muic_data, "muic-attach");
+       FREE_IRQ(muic_data->irq_detach, muic_data, "muic-detach");
+#if IS_ENABLED(CONFIG_MUIC_S2MU004_RID)
+       FREE_IRQ(muic_data->irq_rid_chg, muic_data, "muic-rid_chg");
+#endif
+       FREE_IRQ(muic_data->irq_vbus_on, muic_data, "muic-vbus_on");
+       FREE_IRQ(muic_data->irq_rsvd_attach, muic_data, "muic-rsvd_attach");
+#if IS_ENABLED(CONFIG_MUIC_S2MU004_RID)
+       FREE_IRQ(muic_data->irq_adc_change, muic_data, "muic-adc_change");
+#endif
+       FREE_IRQ(muic_data->irq_av_charge, muic_data, "muic-av_charge");
+       FREE_IRQ(muic_data->irq_vbus_off, muic_data, "muic-vbus_off");
+}
+
+#if IS_ENABLED(CONFIG_OF)
+static int of_s2mu004_muic_dt(struct device *dev,
+                             struct s2mu004_muic_data *muic_data)
+{
+       struct device_node *np, *np_muic;
+       int ret = 0;
+
+       np = dev->parent->of_node;
+       if (!np) {
+               pr_err("%s : could not find np\n", __func__);
+               return -ENODEV;
+       }
+
+       np_muic = of_find_node_by_name(np, "muic");
+       if (!np_muic) {
+               pr_err("%s : could not find muic sub-node np_muic\n", __func__);
+               return -EINVAL;
+       }
+
+/* FIXME */
+#if !IS_ENABLED(CONFIG_MUIC_UART_SWITCH)
+       if (of_gpio_count(np_muic) < 1) {
+               pr_err("%s : could not find muic gpio\n", __func__);
+               muic_data->pdata->gpio_uart_sel = 0;
+       } else
+               muic_data->pdata->gpio_uart_sel = of_get_gpio(np_muic, 0);
+#else
+       muic_data->pdata->uart_addr =
+           (const char *)of_get_property(np_muic, "muic,uart_addr", NULL);
+       muic_data->pdata->uart_txd =
+           (const char *)of_get_property(np_muic, "muic,uart_txd", NULL);
+       muic_data->pdata->uart_rxd =
+           (const char *)of_get_property(np_muic, "muic,uart_rxd", NULL);
+#endif
+
+       return ret;
+}
+#endif /* CONFIG_OF */
+
+static void s2mu004_muic_init_drvdata(struct s2mu004_muic_data *muic_data,
+                                     struct s2mu004_dev *s2mu004,
+                                     struct platform_device *pdev,
+                                     struct s2mu004_platform_data *mfd_pdata)
+{
+       /* save platfom data for gpio control functions */
+       muic_data->s2mu004_dev = s2mu004;
+       muic_data->dev = &pdev->dev;
+       muic_data->i2c = s2mu004->i2c;
+       muic_data->mfd_pdata = mfd_pdata;
+       muic_data->re_detect = 0;
+       muic_data->afc_check = false;
+       muic_data->is_dcd_recheck = false;
+}
+
+static void s2mu004_muic_init_interface(struct s2mu004_muic_data *muic_data,
+                                       struct muic_interface_t *muic_if)
+{
+       struct muic_platform_data *muic_pdata = muic_data->pdata;
+
+       pr_info("%s, muic_if : 0x%p, muic_data : 0x%p\n",
+               __func__, muic_if, muic_data);
+
+       muic_if->muic_data = (void *)muic_data;
+       muic_if->set_com_to_open = s2mu004_if_com_to_open;
+       muic_if->set_switch_to_usb = s2mu004_if_switch_to_usb;
+       muic_if->set_switch_to_uart = s2mu004_if_switch_to_uart;
+       muic_if->get_vbus = s2mu004_if_get_vbus;
+       muic_if->set_cable_state = s2mu004_if_set_cable_state;
+       muic_if->set_otg_detect_en = s2mu004_if_set_otg_detect_en;
+       muic_if->set_dcd_rescan = s2mu004_if_dcd_rescan;
+       muic_if->set_jig_ctrl_on = s2mu004_if_jig_on;
+       muic_if->set_com_to_audio = s2mu004_if_com_to_audio;
+       muic_if->set_com_to_otg = s2mu004_if_com_to_otg;
+       muic_if->set_gpio_uart_sel = s2mu004_if_set_gpio_uart_sel;
+       muic_if->bcd_rescan = s2mu004_if_bcd_rescan;
+       muic_if->control_rid_adc = s2mu004_if_control_rid_adc;
+#if IS_ENABLED(CONFIG_MUIC_S2MU004_RID)
+       muic_if->get_adc = s2mu004_if_get_adc;
+#endif
+#if defined(CONFIG_MUIC_S2MU004_HV)
+       muic_if->set_afc_reset = s2mu004_if_set_afc_reset;
+       muic_if->check_id_err = s2mu004_if_check_id_err;
+       muic_if->reset_hvcontrol_reg = s2mu004_if_reset_hvcontrol_reg;
+       muic_if->check_afc_ready = s2mu004_if_check_afc_ready;
+       muic_if->reset_afc_register = s2mu004_if_reset_afc_register;
+#endif
+       muic_data->if_data = muic_if;
+       muic_pdata->muic_if = muic_if;
+}
+
+static int s2mu004_muic_probe(struct platform_device *pdev)
+{
+       struct s2mu004_dev *s2mu004 = dev_get_drvdata(pdev->dev.parent);
+       struct s2mu004_platform_data *mfd_pdata = dev_get_platdata(s2mu004->dev);
+       struct s2mu004_muic_data *muic_data;
+       struct muic_platform_data *muic_pdata;
+       int ret = 0;
+       struct muic_interface_t *muic_if;
+
+       pr_info("%s start\n", __func__);
+       muic_data = devm_kzalloc(&pdev->dev, sizeof(*muic_data), GFP_KERNEL);
+       if (unlikely(!muic_data)) {
+               pr_err("%s out of memory\n", __func__);
+               ret = -ENOMEM;
+               goto err_return;
+       }
+       static_data = muic_data;
+
+       if (unlikely(!mfd_pdata)) {
+               pr_err("%s failed to get s2mu004 mfd platform data\n",
+                      __func__);
+               ret = -ENOMEM;
+               goto err_kfree1;
+       }
+
+       muic_pdata = muic_core_init(muic_data);
+       if (unlikely(!muic_pdata))
+               goto err_kfree1;
+
+       muic_data->pdata = muic_pdata;
+       muic_if = muic_manager_init(muic_pdata, muic_data);
+       if (!muic_if) {
+               pr_err("%s failed to init muic manager, ret : 0x%X\n",
+                      __func__, ret);
+               goto err_init_manager;
+       }
+       s2mu004_muic_init_interface(muic_data, muic_if);
+       s2mu004_muic_init_drvdata(muic_data, s2mu004, pdev, mfd_pdata);
+#if defined(CONFIG_OF)
+       ret = of_s2mu004_muic_dt(&pdev->dev, muic_data);
+       if (ret < 0)
+               pr_err("no muic dt! ret[%d]\n", ret);
+#endif /* CONFIG_OF */
+
+       mutex_init(&muic_data->muic_mutex);
+       mutex_init(&muic_data->switch_mutex);
+       wake_lock_init(&muic_data->wake_lock, WAKE_LOCK_SUSPEND, "muic_wake");
+       platform_set_drvdata(pdev, muic_data);
+
+       if (muic_data->pdata->init_gpio_cb)
+               ret = muic_data->pdata->init_gpio_cb(muic_data->pdata, get_switch_sel());
+       if (ret) {
+               pr_err("%s failed to init gpio(%d)\n", __func__, ret);
+               goto fail_init_gpio;
+       }
+
+       pr_info("%s: usb_path(%d), uart_path(%d)\n", __func__,
+               muic_pdata->usb_path, muic_pdata->uart_path);
+
+       ret = s2mu004_muic_init_sysfs(muic_data);
+       if (ret) {
+               pr_err("failed to create sysfs\n");
+               goto fail_init_sysfs;
+       }
+
+       ret = s2mu004_init_rev_info(muic_data);
+       if (ret) {
+               pr_err("failed to init muic(%d)\n", ret);
+               goto fail;
+       }
+
+       ret = s2mu004_muic_reg_init(muic_data);
+       if (ret) {
+               pr_err("failed to init muic(%d)\n", ret);
+               goto fail;
+       }
+#if IS_ENABLED(CONFIG_MUIC_S2MU004_HV)
+       /* initial hv cable detection */
+       if (muic_data->is_afc_muic_ready)
+               s2mu004_hv_muic_init_detect(muic_data);
+
+       s2mu004_hv_muic_initialize(muic_data);
+#endif /* CONFIG_MUIC_S2MU004_HV */
+
+       if (muic_pdata->is_rustproof) {
+               pr_err("%s rustproof is enabled\n", __func__);
+               s2mu004_muic_com_to_open(muic_data);
+       }
+#if IS_ENABLED(CONFIG_MUIC_S2MU004_HV)
+       if (get_afc_mode() == CH_MODE_AFC_DISABLE_VAL) {
+               pr_info("AFC mode disabled\n");
+               muic_data->pdata->afc_disable = true;
+       } else {
+               pr_info("AFC mode enabled\n");
+               muic_data->pdata->afc_disable = false;
+       }
+#endif /* CONFIG_MUIC_S2MU004_HV */
+
+       ret = s2mu004_muic_irq_init(muic_data);
+       if (ret) {
+               pr_err("%s failed to init irq(%d)\n", __func__, ret);
+               goto fail_init_irq;
+       }
+#if IS_ENABLED(CONFIG_MUIC_S2MU004_HV)
+       ret = s2mu004_afc_muic_irq_init(muic_data);
+       if (ret < 0) {
+               pr_err("%s Failed to initialize HV MUIC irq:%d\n",
+                      __func__, ret);
+               s2mu004_hv_muic_free_irqs(muic_data);
+       }
+#endif /* CONFIG_MUIC_S2MU004_HV */
+
+       /* initial cable detection */
+       s2mu004_muic_set_int_mask(muic_data, false);
+#if IS_ENABLED(CONFIG_SEC_FACTORY)
+       s2mu004_muic_init_otg_reg(muic_data);
+#endif
+
+       pr_info("%s muic_if->opmode(%d)\n", __func__, muic_if->opmode);
+
+       INIT_DELAYED_WORK(&muic_data->dcd_recheck, s2mu004_muic_dcd_recheck);
+
+       s2mu004_muic_attach_isr(-1, muic_data);
+
+#if IS_ENABLED(CONFIG_MUIC_S2MU004_RID)
+       if (!s2mu004_muic_get_vbus_state(muic_data)) {
+               pr_info("%s : init adc : 0x%X\n", __func__,
+                       s2mu004_muic_recheck_adc(muic_data));
+       }
+#endif
+
+       return 0;
+
+fail_init_irq:
+fail:
+#ifdef CONFIG_SEC_SYSFS
+       s2mu004_muic_deinit_sysfs(muic_data);
+#endif
+fail_init_sysfs:
+fail_init_gpio:
+       mutex_destroy(&muic_data->muic_mutex);
+err_init_manager:
+err_kfree1:
+err_return:
+       return ret;
+}
+
+/* if need to set s2mu004 pdata */
+static const struct of_device_id s2mu004_muic_match_table[] = {
+       {.compatible = "samsung,s2mu004-muic",},
+       {},
+};
+
+static int s2mu004_muic_remove(struct platform_device *pdev)
+{
+       struct s2mu004_muic_data *muic_data = platform_get_drvdata(pdev);
+
+       if (muic_data) {
+               pr_info("%s\n", __func__);
+
+#ifdef CONFIG_SEC_SYSFS
+               s2mu004_muic_deinit_sysfs(muic_data);
+#endif
+               muic_manager_exit(muic_data->if_data);
+               muic_core_exit(muic_data->pdata);
+
+               disable_irq_wake(muic_data->i2c->irq);
+               s2mu004_muic_free_irqs(muic_data);
+               mutex_destroy(&muic_data->muic_mutex);
+               mutex_destroy(&muic_data->switch_mutex);
+               i2c_set_clientdata(muic_data->i2c, NULL);
+       }
+
+       return 0;
+}
+
+static void s2mu004_muic_shutdown(struct platform_device *pdev)
+{
+       struct s2mu004_muic_data *muic_data = platform_get_drvdata(pdev);
+       int ret;
+
+       pr_info("%s\n", __func__);
+
+       if (!muic_data->i2c) {
+               pr_err("%s no muic i2c client\n", __func__);
+               return;
+       }
+#if IS_ENABLED(CONFIG_MUIC_S2MU004_HV)
+       s2mu004_hv_muic_remove(muic_data);
+#endif
+
+       ret = s2mu004_muic_com_to_open(muic_data);
+       if (ret < 0)
+               pr_err("fail to open mansw\n");
+
+       /* set auto sw mode before shutdown to make sure device goes into */
+       /* LPM charging when TA or USB is connected during off state */
+       pr_info("muic auto detection enable\n");
+       ret = s2mu004_muic_set_manual_sw(muic_data, true);
+       if (ret < 0) {
+               pr_err("%s fail to update reg\n", __func__);
+               return;
+       }
+}
+
+#if IS_ENABLED(CONFIG_PM)
+static int s2mu004_muic_suspend(struct device *dev)
+{
+       struct s2mu004_muic_data *muic_data = dev_get_drvdata(dev);
+       struct muic_platform_data *muic_pdata = muic_data->pdata;
+
+       muic_pdata->suspended = true;
+
+       return 0;
+}
+
+static int s2mu004_muic_resume(struct device *dev)
+{
+       struct s2mu004_muic_data *muic_data = dev_get_drvdata(dev);
+       struct muic_platform_data *muic_pdata = muic_data->pdata;
+
+       muic_pdata->suspended = false;
+
+       if (muic_pdata->need_to_noti) {
+               if (muic_pdata->attached_dev) {
+                       MUIC_SEND_NOTI_ATTACH(muic_pdata->attached_dev);
+               } else {
+                       MUIC_SEND_NOTI_DETACH(muic_pdata->attached_dev);
+               }
+
+               muic_pdata->need_to_noti = false;
+       }
+
+       return 0;
+}
+#else
+#define s2mu004_muic_suspend NULL
+#define s2mu004_muic_resume NULL
+#endif
+
+static SIMPLE_DEV_PM_OPS(s2mu004_muic_pm_ops, s2mu004_muic_suspend,
+                        s2mu004_muic_resume);
+
+static struct platform_driver s2mu004_muic_driver = {
+       .probe = s2mu004_muic_probe,
+       .remove = s2mu004_muic_remove,
+       .shutdown = s2mu004_muic_shutdown,
+       .driver = {
+                  .name = "s2mu004-muic",
+                  .owner = THIS_MODULE,
+                  .of_match_table = s2mu004_muic_match_table,
+#if IS_ENABLED(CONFIG_PM)
+                  .pm = &s2mu004_muic_pm_ops,
+#endif
+                  },
+};
+
+static int __init s2mu004_muic_init(void)
+{
+       return platform_driver_register(&s2mu004_muic_driver);
+}
+
+module_init(s2mu004_muic_init);
+
+static void __exit s2mu004_muic_exit(void)
+{
+       platform_driver_unregister(&s2mu004_muic_driver);
+}
+
+module_exit(s2mu004_muic_exit);
+
+MODULE_DESCRIPTION("Samsung S2MU004 Micro USB IC driver");
+MODULE_LICENSE("GPL");
index bc03502265959b1f920f55cf4d1020c1488bd49d..2e63894c2b6ccb1c9db2ef6ca3f6ae582bb300de 100644 (file)
 #include <linux/alarmtimer.h>
 
 #if defined(CONFIG_MUIC_NOTIFIER)
-#include <linux/muic/muic_notifier.h>
+#include <linux/muic/s2mu004-muic-notifier.h>
 #include <linux/muic/muic.h>
 #endif /* CONFIG_MUIC_NOTIFIER */
 
 #if defined(CONFIG_IFCONN_NOTIFIER)
 #include <linux/ifconn/ifconn_notifier.h>
 #include <linux/ifconn/ifconn_manager.h>
-#include <linux/muic/muic_notifier.h>
+#include <linux/muic/s2mu004-muic-notifier.h>
 #include <linux/muic/muic.h>
 #endif
 
diff --git a/include/linux/ccic/ccic_notifier.h b/include/linux/ccic/ccic_notifier.h
new file mode 100644 (file)
index 0000000..d6c700f
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ * 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 __CCIC_NOTIFIER_H__
+#define __CCIC_NOTIFIER_H__
+
+/* CCIC notifier call sequence,
+ * largest priority number device will be called first. */
+typedef enum {
+       CCIC_NOTIFY_DEV_INITIAL = 0,
+       CCIC_NOTIFY_DEV_USB,
+       CCIC_NOTIFY_DEV_BATTERY,
+       CCIC_NOTIFY_DEV_PDIC,
+       CCIC_NOTIFY_DEV_MUIC,
+       CCIC_NOTIFY_DEV_CCIC,
+#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER
+       CCIC_NOTIFY_DEV_MANAGER,
+#endif
+       CCIC_NOTIFY_DEV_DP,
+       CCIC_NOTIFY_DEV_USB_DP,
+} ccic_notifier_device_t;
+
+typedef enum {
+       CCIC_NOTIFY_ID_INITIAL = 0,
+       CCIC_NOTIFY_ID_ATTACH,
+       CCIC_NOTIFY_ID_RID,
+       CCIC_NOTIFY_ID_USB,
+#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER
+       CCIC_NOTIFY_ID_POWER_STATUS,
+#endif
+       CCIC_NOTIFY_ID_WATER,
+       CCIC_NOTIFY_ID_VCONN,
+       CCIC_NOTIFY_ID_DP_CONNECT,
+       CCIC_NOTIFY_ID_DP_HPD,
+       CCIC_NOTIFY_ID_DP_LINK_CONF,
+       CCIC_NOTIFY_ID_USB_DP,
+       CCIC_NOTIFY_ID_ROLE_SWAP,
+       CCIC_NOTIFY_ID_FAC,
+       CCIC_NOTIFY_ID_CC_PIN_STATUS,
+} ccic_notifier_id_t;
+
+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;
+#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER
+       void *pd;
+#endif
+} CC_NOTI_TYPEDEF;
+
+/* ID = 1 : Attach */
+typedef struct
+{
+       uint64_t src:4;
+       uint64_t dest:4;
+       uint64_t id:8;
+       uint64_t attach:16;
+       uint64_t rprd:16;
+       uint64_t cable_type:16;
+#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER
+       void *pd;
+#endif
+} CC_NOTI_ATTACH_TYPEDEF;
+
+typedef enum {
+       CCIC_NOTIFY_DETACH = 0,
+       CCIC_NOTIFY_ATTACH,
+} ccic_notifier_attach_t;
+
+typedef enum {
+       CCIC_NOTIFY_DEVICE = 0,
+       CCIC_NOTIFY_HOST,
+} ccic_notifier_attach_rprd_t;
+
+typedef enum {
+       CCIC_NOTIFY_LOW = 0,
+       CCIC_NOTIFY_HIGH,
+       CCIC_NOTIFY_IRQ,
+} ccic_notifier_dp_hpd_t;
+
+typedef enum {
+       CCIC_NOTIFY_DP_PIN_UNKNOWN = 0,
+       CCIC_NOTIFY_DP_PIN_A,
+       CCIC_NOTIFY_DP_PIN_B,
+       CCIC_NOTIFY_DP_PIN_C,
+       CCIC_NOTIFY_DP_PIN_D,
+       CCIC_NOTIFY_DP_PIN_E,
+       CCIC_NOTIFY_DP_PIN_F,
+} ccic_notifier_dp_pinconf_t;
+
+#if !defined(CONFIG_CCIC_S2MM005)
+/* Function Status from s2mm005 definition */
+typedef enum {
+       State_PE_Initial_detach = 0,
+       State_PE_SRC_Send_Capabilities = 3,
+       State_PE_SNK_Wait_for_Capabilities = 17,
+} ccic_notifier_pd_state_t;
+#endif
+
+/* ID = 2 : RID */
+typedef struct
+{
+       uint64_t src:4;
+       uint64_t dest:4;
+       uint64_t id:8;
+       uint64_t rid:16;
+       uint64_t sub2:16;
+       uint64_t sub3:16;
+#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER
+       void *pd;
+#endif
+} CC_NOTI_RID_TYPEDEF;
+
+typedef enum {
+       RID_UNDEFINED = 0,
+       RID_000K,
+       RID_001K,
+       RID_255K,
+       RID_301K,
+       RID_523K,
+       RID_619K,
+       RID_OPEN,
+} ccic_notifier_rid_t;
+
+/* ID = 3 : USB status */
+typedef struct
+{
+       uint64_t src:4;
+       uint64_t dest:4;
+       uint64_t id:8;
+       uint64_t attach:16;
+       uint64_t drp:16;
+       uint64_t sub3:16;
+#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER
+       void *pd;
+#endif
+} CC_NOTI_USB_STATUS_TYPEDEF;
+
+typedef struct
+{
+       uint64_t src:4;
+       uint64_t dest:4;
+       uint64_t id:8;
+       uint64_t is_connect:16;
+       uint64_t hs_connect:16;
+       uint64_t reserved:16;
+#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER
+       void *pd;
+#endif
+} USB_DP_NOTI_TYPEDEF;
+
+typedef enum
+{
+       USB_STATUS_NOTIFY_DETACH = 0,
+       USB_STATUS_NOTIFY_ATTACH_DFP = 1, // Host
+       USB_STATUS_NOTIFY_ATTACH_UFP = 2, // Device
+       USB_STATUS_NOTIFY_ATTACH_DRP = 3, // Dual role
+       USB_STATUS_NOTIFY_ATTACH_HPD = 4, // DP : Hot Plugged Detect
+} USB_STATUS;
+
+/* TODO:  */
+struct ccic_notifier_struct {
+       CC_NOTI_TYPEDEF ccic_template;
+       struct blocking_notifier_head notifier_call_chain;
+};
+
+#define CCIC_NOTIFIER_BLOCK(name)      \
+       struct notifier_block (name)
+
+extern int ccic_notifier_notify(CC_NOTI_TYPEDEF *, void *, int);
+//extern void ccic_notifier_255K_test(void);
+
+/* ccic notifier register/unregister API
+ * for used any where want to receive ccic attached device attach/detach. */
+extern int ccic_notifier_register(struct notifier_block *nb,
+               notifier_fn_t notifier, ccic_notifier_device_t listener);
+extern int ccic_notifier_unregister(struct notifier_block *nb);
+extern int ccic_notifier_init(void);
+
+#define CCIC_NOTI_DEST_NUM     (10)
+#define CCIC_NOTI_ID_NUM       (14)
+#define CCIC_NOTI_RID_NUM      (8)
+#define CCIC_NOTI_USB_STATUS_NUM (5)
+#define CCIC_NOTI_PIN_STATUS_NUM       (8)
+
+extern char CCIC_NOTI_DEST_Print[CCIC_NOTI_DEST_NUM][10];
+extern char CCIC_NOTI_ID_Print[CCIC_NOTI_ID_NUM][20];
+extern char CCIC_NOTI_RID_Print[CCIC_NOTI_RID_NUM][15];
+extern char CCIC_NOTI_USB_STATUS_Print[CCIC_NOTI_USB_STATUS_NUM][20];
+#endif /* __CCIC_NOTIFIER_H__ */
+
diff --git a/include/linux/ifconn/ifconn_manager.h b/include/linux/ifconn/ifconn_manager.h
new file mode 100644 (file)
index 0000000..d7b1fde
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ * include/linux/ifconn/ifconn_manager.h
+ *
+ * header file supporting CCIC notifier call chain information
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ * Sejong Park <sejong.park@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 __IFCONN_MANAGER_H__
+#define __IFCONN_MANAGER_H__
+
+typedef enum {
+/* MUIC */
+       IFCONN_CABLE_TYPE_MUIC_NONE = 0,
+       IFCONN_CABLE_TYPE_MUIC_DOCK,
+       IFCONN_CABLE_TYPE_MUIC_MHL,
+       IFCONN_CABLE_TYPE_MUIC_USB,
+       IFCONN_CABLE_TYPE_MUIC_TSP,
+       IFCONN_CABLE_TYPE_MUIC_CHARGER,
+       IFCONN_CABLE_TYPE_MUIC_CPUIDLE,
+       IFCONN_CABLE_TYPE_MUIC_CPUFREQ,
+       IFCONN_CABLE_TYPE_MUIC_TIMEOUT_OPEN_DEVICE,
+
+/* CCIC */
+       IFCONN_CABLE_TYPE_CCIC_INITIAL = 20,
+       IFCONN_CABLE_TYPE_CCIC_MUIC,
+       IFCONN_CABLE_TYPE_CCIC_USB,
+       IFCONN_CABLE_TYPE_CCIC_BATTERY,
+       IFCONN_CABLE_TYPE_CCIC_DP,
+       IFCONN_CABLE_TYPE_CCIC_USBDP,
+       IFCONN_CABLE_TYPE_CCIC_SENSORHUB,
+
+/* VBUS */
+       IFCONN_CABLE_TYPE_VBUS_USB = 30,
+       IFCONN_CABLE_TYPE_VBUS_CHARGER,
+} ifconn_cable_type_t;
+
+typedef enum {
+       IFCONN_PD_USB_TYPE,
+       IFCONN_PD_TA_TYPE,
+} ifconn_pd_usb_state_t;
+
+typedef enum {
+       IFCONN_MANAGER_USB_STATUS_DETACH = 0,
+       IFCONN_MANAGER_USB_STATUS_ATTACH_DFP = 1, /* Host */
+       IFCONN_MANAGER_USB_STATUS_ATTACH_UFP = 2, /* Device */
+       IFCONN_MANAGER_USB_STATUS_ATTACH_DRP = 3, /* Dual role */
+       IFCONN_MANAGER_USB_STATUS_ATTACH_HPD = 4, /* DP : Hot Plugged Detect */
+} ifconn_manager_usb_status_t;
+
+typedef enum {
+       IFCONN_MANAGER_VBUS_STATUS_UNKNOWN = 0,
+       IFCONN_MANAGER_VBUS_STATUS_LOW,
+       IFCONN_MANAGER_VBUS_STATUS_HIGH,
+} ifconn_manager_vbus_status_t;
+
+typedef enum {
+       IFCONN_MANAGER_RID_UNDEFINED = 0,
+       IFCONN_MANAGER_RID_000K,
+       IFCONN_MANAGER_RID_001K,
+       IFCONN_MANAGER_RID_255K,
+       IFCONN_MANAGER_RID_301K,
+       IFCONN_MANAGER_RID_523K,
+       IFCONN_MANAGER_RID_619K,
+       IFCONN_MANAGER_RID_OPEN,
+} ifconn_manager_rid_t;
+
+struct ifconn_manager_platform_data {
+       void (*initial_check)(void);
+       void (*select_pdo)(int);
+       const char *usbpd_name;
+       const char *muic_name;
+};
+
+#define IFCONN_SEND_NOTI(dest, id, event, data) \
+{      \
+       int ret;        \
+       ret = ifconn_notifier_notify( \
+                                       IFCONN_NOTIFY_MANAGER,  \
+                                       IFCONN_NOTIFY_##dest,   \
+                                       IFCONN_NOTIFY_ID_##id,  \
+                                       IFCONN_NOTIFY_EVENT_##event,    \
+                                       IFCONN_NOTIFY_PARAM_DATA,       \
+                                       data);  \
+       if (ret < 0) {  \
+               pr_err("%s: Fail to send noti : "#dest" "#id"\n", \
+                               __func__);      \
+       }       \
+}
+
+#define IFCONN_SEND_TEMPLATE_NOTI(data) \
+{      \
+       int ret;        \
+       struct ifconn_notifier_template *template       \
+               = (struct ifconn_notifier_template *)data;      \
+       ret = ifconn_notifier_notify( \
+                                       template->src,  \
+                                       template->dest, \
+                                       template->id,   \
+                                       template->event,        \
+                                       IFCONN_NOTIFY_PARAM_TEMPLATE,   \
+                                       &template);     \
+       if (ret < 0) {  \
+               pr_err("%s: Fail to send noti\n", \
+                               __func__);      \
+       }       \
+}
+
+#define IFCONN_SEND_TEMPLATE_UP_NOTI(nd) \
+{      \
+       int ret;        \
+       struct ifconn_notifier_template *template       \
+               = (struct ifconn_notifier_template *)nd;        \
+               pr_info("%s: dbg, line : %d\n", __func__, __LINE__);\
+       ret = ifconn_notifier_notify( \
+                                       template->src,  \
+                                       template->dest, \
+                                       template->id,   \
+                                       template->event,        \
+                                       IFCONN_NOTIFY_PARAM_TEMPLATE,   \
+                                       nd);    \
+       if (ret < 0) {  \
+               pr_err("%s: Fail to send noti\n", \
+                               __func__);      \
+       }       \
+}
+
+struct ifconn_manager_template {
+       struct ifconn_notifier_template node;
+       void *rp;
+       void *np;
+};
+
+struct ifconn_manager_data {
+       struct ifconn_manager_platform_data *pdata;
+       struct notifier_block           nb;
+       struct ifconn_notifier_template *template;
+       struct ifconn_manager_template *hp;
+       struct ifconn_manager_template *tp;
+       int template_cnt;
+       struct device   *dev;
+       struct mutex noti_mutex;
+       struct mutex workqueue_mutex;
+       struct mutex enqueue_mutex;
+       struct work_struct noti_work;
+
+       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;
+       /* USB_STATUS_NOTIFY_DETACH, UFP, DFP, DRP, NO_USB */
+       int ccic_attach_state;
+       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 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;
+       int dp_attach_state;
+       int dp_cable_type;
+       int dp_hpd_state;
+       int dp_is_connect;
+       int dp_hs_connect;
+       int dp_check_done;
+};
+
+extern void _ifconn_show_attr(struct ifconn_notifier_template *t);
+#endif /* __IFCONN_MANAGER_H__ */
diff --git a/include/linux/ifconn/ifconn_notifier.h b/include/linux/ifconn/ifconn_notifier.h
new file mode 100644 (file)
index 0000000..0e5bbc1
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+ * include/linux/ifconn/ifconn_notifier.h
+ *
+ * header file supporting IFCONN notifier call chain information
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ * Sejong Park <sejong123.park@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 __IFCONN_MANAGER_NOTIFIER_H__
+#define __IFCONN_MANAGER_NOTIFIER_H__
+
+#include <linux/notifier.h>
+
+#define IFCONN_MANAGER_NOTIFIER_BLOCK(name)    \
+       struct notifier_block(name)
+
+#define MAX 7
+
+typedef enum {
+       IFCONN_NOTIFY_MANAGER = 0,
+       IFCONN_NOTIFY_USB,
+       IFCONN_NOTIFY_BATTERY,
+       IFCONN_NOTIFY_PDIC,
+       IFCONN_NOTIFY_MUIC,
+       IFCONN_NOTIFY_VBUS,
+       IFCONN_NOTIFY_CCIC,
+       IFCONN_NOTIFY_DP,
+       IFCONN_NOTIFY_USB_DP,
+       IFCONN_NOTIFY_ALL,
+       IFCONN_NOTIFY_MAX,
+} ifconn_notifier_t;
+
+typedef enum {
+       IFCONN_NOTIFY_PARAM_DATA,
+       IFCONN_NOTIFY_PARAM_TEMPLATE
+} ifconn_notifier_data_param_t;
+
+enum {
+       IFCONN_NOTIFY_LOW = 0,
+       IFCONN_NOTIFY_HIGH,
+       IFCONN_NOTIFY_IRQ,
+};
+
+typedef enum {
+       IFCONN_NOTIFY_ID_INITIAL = 0,
+       IFCONN_NOTIFY_ID_ATTACH,
+       IFCONN_NOTIFY_ID_DETACH,
+       IFCONN_NOTIFY_ID_RID,
+       IFCONN_NOTIFY_ID_USB,
+       IFCONN_NOTIFY_ID_POWER_STATUS,
+       IFCONN_NOTIFY_ID_WATER,
+       IFCONN_NOTIFY_ID_OTG,
+       IFCONN_NOTIFY_ID_TA,
+       IFCONN_NOTIFY_ID_VCONN,
+       IFCONN_NOTIFY_ID_DP_CONNECT,
+       IFCONN_NOTIFY_ID_DP_HPD,
+       IFCONN_NOTIFY_ID_DP_LINK_CONF,
+       IFCONN_NOTIFY_ID_USB_DP,
+       IFCONN_NOTIFY_ID_ROLE_SWAP,
+       IFCONN_NOTIFY_ID_SELECT_PDO,
+       IFCONN_NOTIFY_ID_VBUS,
+} ifconn_notifier_id_t;
+
+typedef enum {
+       IFCONN_NOTIFY_DETACH = 0,
+       IFCONN_NOTIFY_ATTACH,
+} ifconn_notifier_attach_t;
+
+typedef enum {
+       IFCONN_NOTIFY_VBUS_UNKNOWN = 0,
+       IFCONN_NOTIFY_VBUS_LOW,
+       IFCONN_NOTIFY_VBUS_HIGH,
+} ifconn_notifier_vbus_t;
+
+typedef enum {
+       IFCONN_NOTIFY_STATUS_NOT_READY = 0,
+       IFCONN_NOTIFY_STATUS_NOT_READY_DETECT,
+       IFCONN_NOTIFY_STATUS_READY,
+} ifconn_notifier_vbus_stat_t;
+
+typedef enum {
+       IFCONN_NOTIFY_EVENT_DETACH = 0,
+       IFCONN_NOTIFY_EVENT_ATTACH,
+       IFCONN_NOTIFY_EVENT_USB_ATTACH_DFP, // Host
+       IFCONN_NOTIFY_EVENT_USB_ATTACH_UFP, // Device
+       IFCONN_NOTIFY_EVENT_USB_ATTACH_DRP, // Dual role
+       IFCONN_NOTIFY_EVENT_USB_ATTACH_HPD, // DP : Hot Plugged Detect
+       IFCONN_NOTIFY_EVENT_PD_SINK,
+       IFCONN_NOTIFY_EVENT_PD_SOURCE,
+       IFCONN_NOTIFY_EVENT_PD_SINK_CAP,
+       IFCONN_NOTIFY_EVENT_DP_LOW,
+       IFCONN_NOTIFY_EVENT_DP_HIGH,
+       IFCONN_NOTIFY_EVENT_DP_IRQ,
+       IFCONN_NOTIFY_EVENT_MUIC_LOGICALLY_DETACH,
+       IFCONN_NOTIFY_EVENT_MUIC_LOGICALLY_ATTACH,
+       IFCONN_NOTIFY_EVENT_IN_HURRY,
+} ifconn_notifier_event_t;
+
+typedef enum {
+       IFCONN_NOTIFY_DP_PIN_UNKNOWN = 0,
+       IFCONN_NOTIFY_DP_PIN_A,
+       IFCONN_NOTIFY_DP_PIN_B,
+       IFCONN_NOTIFY_DP_PIN_C,
+       IFCONN_NOTIFY_DP_PIN_D,
+       IFCONN_NOTIFY_DP_PIN_E,
+       IFCONN_NOTIFY_DP_PIN_F,
+} ifconn_notifier_dp_pinconf_t;
+
+typedef struct _ifconn_power_list {
+       int max_voltage;
+       int max_current;
+} IFCONN_POWER_LIST;
+
+struct ifconn_pd_sink_status {
+       IFCONN_POWER_LIST power_list[MAX];
+       int available_pdo_num; // the number of available PDO
+       int selected_pdo_num; // selected number of PDO to change
+       int current_pdo_num; // current number of PDO
+       unsigned int rp_currentlvl; // rp current level by ccic
+};
+
+struct ifconn_notifier_template {
+       uint64_t src:4;
+       uint64_t dest:4;
+       uint64_t id:8;
+       uint64_t rid:8;
+       uint64_t attach:16;
+       uint64_t rprd:16;
+       uint64_t cable_type:16;
+       uint64_t event:16;
+       uint64_t drp:16;
+       uint64_t sub1:16;
+       uint64_t sub2:16;
+       uint64_t sub3:16;
+       uint64_t up_src:16;
+       uint64_t data_size:16;
+       ifconn_notifier_vbus_t vbus_type;
+       void *data;
+       void *pdata;
+       void *dest_dev;
+};
+
+struct ifconn_notifier {
+       struct notifier_block *nb[IFCONN_NOTIFY_MAX];
+       struct ifconn_notifier_template ifconn_template[IFCONN_NOTIFY_MAX];
+       struct delayed_work notify_work;
+       bool sent[IFCONN_NOTIFY_MAX];
+       void *dev;
+       struct blocking_notifier_head notifier_call_chain;
+};
+
+int ifconn_notifier_register(struct notifier_block *nb,
+                                               notifier_fn_t notifier,
+                                               ifconn_notifier_t listener,
+                                               ifconn_notifier_t src);
+
+int ifconn_notifier_unregister(ifconn_notifier_t src,
+                                                                       ifconn_notifier_t listener);
+
+int ifconn_notifier_notify(ifconn_notifier_t src,
+                                                               ifconn_notifier_t dest,
+                                                               int noti_id,
+                                                               int event,
+                                                               ifconn_notifier_data_param_t param_type,
+                                                               void *data);
+
+#endif /* __IFCONN_MANAGER_NOTIFIER_H__ */
diff --git a/include/linux/muic/muic.h b/include/linux/muic/muic.h
new file mode 100644 (file)
index 0000000..878806c
--- /dev/null
@@ -0,0 +1,266 @@
+/*
+ * include/linux/muic/muic.h
+ *
+ * header file supporting MUIC common information
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ * Seoyoung Jeong <seo0.jeong@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 __MUIC_H__
+#define __MUIC_H__
+
+/* Status of IF PMIC chip (suspend and resume) */
+enum {
+       MUIC_SUSPEND            = 0,
+       MUIC_RESUME,
+};
+
+/* MUIC Interrupt */
+enum {
+       MUIC_INTR_DETACH        = 0,
+       MUIC_INTR_ATTACH
+};
+
+enum muic_op_mode {
+       OPMODE_MUIC = 0<<0,
+       OPMODE_CCIC = 1<<0,
+};
+
+/* MUIC Dock Observer Callback parameter */
+enum {
+       MUIC_DOCK_DETACHED      = 0,
+       MUIC_DOCK_DESKDOCK      = 1,
+       MUIC_DOCK_CARDOCK       = 2,
+       MUIC_DOCK_AUDIODOCK     = 101,
+       MUIC_DOCK_SMARTDOCK     = 102,
+       MUIC_DOCK_HMT           = 105,
+       MUIC_DOCK_ABNORMAL      = 106,
+       MUIC_DOCK_GAMEPAD       = 107,
+       MUIC_DOCK_GAMEPAD_WITH_EARJACK  = 108,
+};
+
+/* MUIC Path */
+enum {
+       MUIC_PATH_USB_AP        = 0,
+       MUIC_PATH_USB_CP,
+       MUIC_PATH_UART_AP,
+       MUIC_PATH_UART_CP,
+       MUIC_PATH_OPEN,
+       MUIC_PATH_AUDIO,
+};
+
+#ifdef CONFIG_MUIC_HV_FORCE_LIMIT
+enum {
+       HV_9V = 0,
+       HV_5V,
+};
+#endif
+
+/* bootparam SWITCH_SEL */
+enum {
+       SWITCH_SEL_USB_MASK     = 0x1,
+       SWITCH_SEL_UART_MASK    = 0x2,
+       SWITCH_SEL_RUSTPROOF_MASK       = 0x8,
+       SWITCH_SEL_AFC_DISABLE_MASK     = 0x100,
+};
+
+/* bootparam CHARGING_MODE */
+enum {
+       CH_MODE_AFC_DISABLE_VAL = 0x31, /* char '1' */
+};
+
+/* MUIC ADC table */
+typedef enum {
+       ADC_GND                 = 0x00,
+       ADC_SEND_END            = 0x01, /* 0x00001 2K ohm */
+       ADC_REMOTE_S11          = 0x0c, /* 0x01100 20.5K ohm */
+       ADC_REMOTE_S12          = 0x0d, /* 0x01101 24.07K ohm */
+       ADC_RESERVED_VZW        = 0x0e, /* 0x01110 28.7K ohm */
+       ADC_INCOMPATIBLE_VZW    = 0x0f, /* 0x01111 34K ohm */
+       ADC_SMARTDOCK           = 0x10, /* 0x10000 40.2K ohm */
+       ADC_RDU_TA              = 0x10, /* 0x10000 40.2K ohm */
+       ADC_HMT                 = 0x11, /* 0x10001 49.9K ohm */
+       ADC_AUDIODOCK           = 0x12, /* 0x10010 64.9K ohm */
+       ADC_USB_LANHUB          = 0x13, /* 0x10011 80.07K ohm */
+       ADC_CHARGING_CABLE      = 0x14, /* 0x10100 102K ohm */
+       ADC_UNIVERSAL_MMDOCK    = 0x15, /* 0x10101 121K ohm */
+       ADC_GAMEPAD             = 0x15, /* 0x10101 121K ohm */
+       ADC_UART_CABLE          = 0x16, /* 0x10110 150K ohm */
+       ADC_CEA936ATYPE1_CHG    = 0x17, /* 0x10111 200K ohm */
+       ADC_JIG_USB_OFF         = 0x18, /* 0x11000 255K ohm */
+       ADC_JIG_USB_ON          = 0x19, /* 0x11001 301K ohm */
+       ADC_DESKDOCK            = 0x1a, /* 0x11010 365K ohm */
+       ADC_CEA936ATYPE2_CHG    = 0x1b, /* 0x11011 442K ohm */
+       ADC_JIG_UART_OFF        = 0x1c, /* 0x11100 523K ohm */
+       ADC_JIG_UART_ON         = 0x1d, /* 0x11101 619K ohm */
+       ADC_AUDIOMODE_W_REMOTE  = 0x1e, /* 0x11110 1000K ohm */
+       ADC_OPEN                = 0x1f,
+       ADC_OPEN_219            = 0xfb, /* ADC open or 219.3K ohm */
+       ADC_219                 = 0xfc, /* ADC open or 219.3K ohm */
+
+       ADC_UNDEFINED           = 0xfd, /* Undefied range */
+       ADC_DONTCARE            = 0xfe, /* ADC don't care for MHL */
+       ADC_ERROR               = 0xff, /* ADC value read error */
+} muic_adc_t;
+
+/* MUIC attached device type */
+typedef enum {
+       ATTACHED_DEV_NONE_MUIC = 0,
+
+       ATTACHED_DEV_USB_MUIC,
+       ATTACHED_DEV_CDP_MUIC,
+       ATTACHED_DEV_OTG_MUIC,
+       ATTACHED_DEV_TA_MUIC,
+       ATTACHED_DEV_UNOFFICIAL_MUIC,
+       ATTACHED_DEV_UNOFFICIAL_TA_MUIC,
+       ATTACHED_DEV_UNOFFICIAL_ID_MUIC,
+       ATTACHED_DEV_UNOFFICIAL_ID_TA_MUIC,
+       ATTACHED_DEV_UNOFFICIAL_ID_ANY_MUIC,
+       ATTACHED_DEV_UNOFFICIAL_ID_USB_MUIC,
+
+       ATTACHED_DEV_UNOFFICIAL_ID_CDP_MUIC,
+       ATTACHED_DEV_UNDEFINED_CHARGING_MUIC,
+       ATTACHED_DEV_DESKDOCK_MUIC,
+       ATTACHED_DEV_UNKNOWN_VB_MUIC,
+       ATTACHED_DEV_DESKDOCK_VB_MUIC,
+       ATTACHED_DEV_CARDOCK_MUIC,
+       ATTACHED_DEV_JIG_UART_OFF_MUIC,
+       ATTACHED_DEV_JIG_UART_OFF_VB_MUIC,      /* VBUS enabled */
+       ATTACHED_DEV_JIG_UART_OFF_VB_OTG_MUIC,  /* for otg test */
+       ATTACHED_DEV_JIG_UART_OFF_VB_FG_MUIC,   /* for fuelgauge test */
+
+       ATTACHED_DEV_JIG_UART_ON_MUIC,
+       ATTACHED_DEV_JIG_UART_ON_VB_MUIC,       /* VBUS enabled */
+       ATTACHED_DEV_JIG_USB_OFF_MUIC,
+       ATTACHED_DEV_JIG_USB_ON_MUIC,
+       ATTACHED_DEV_SMARTDOCK_MUIC,
+       ATTACHED_DEV_SMARTDOCK_VB_MUIC,
+       ATTACHED_DEV_SMARTDOCK_TA_MUIC,
+       ATTACHED_DEV_SMARTDOCK_USB_MUIC,
+       ATTACHED_DEV_UNIVERSAL_MMDOCK_MUIC,
+       ATTACHED_DEV_AUDIODOCK_MUIC,
+
+       ATTACHED_DEV_MHL_MUIC,
+       ATTACHED_DEV_CHARGING_CABLE_MUIC,
+       ATTACHED_DEV_AFC_CHARGER_PREPARE_MUIC,
+       ATTACHED_DEV_AFC_CHARGER_PREPARE_DUPLI_MUIC,
+       ATTACHED_DEV_AFC_CHARGER_5V_MUIC,
+       ATTACHED_DEV_AFC_CHARGER_5V_DUPLI_MUIC,
+       ATTACHED_DEV_AFC_CHARGER_9V_MUIC,
+       ATTACHED_DEV_AFC_CHARGER_9V_DUPLI_MUIC,
+       ATTACHED_DEV_AFC_CHARGER_12V_MUIC,
+       ATTACHED_DEV_AFC_CHARGER_12V_DUPLI_MUIC,
+
+       ATTACHED_DEV_AFC_CHARGER_ERR_V_MUIC,
+       ATTACHED_DEV_AFC_CHARGER_ERR_V_DUPLI_MUIC,
+       ATTACHED_DEV_QC_CHARGER_PREPARE_MUIC,
+       ATTACHED_DEV_QC_CHARGER_5V_MUIC,
+       ATTACHED_DEV_QC_CHARGER_ERR_V_MUIC,
+       ATTACHED_DEV_QC_CHARGER_9V_MUIC,
+       ATTACHED_DEV_HV_ID_ERR_UNDEFINED_MUIC,
+       ATTACHED_DEV_HV_ID_ERR_UNSUPPORTED_MUIC,
+       ATTACHED_DEV_HV_ID_ERR_SUPPORTED_MUIC,
+       ATTACHED_DEV_HMT_MUIC,
+
+       ATTACHED_DEV_VZW_ACC_MUIC,
+       ATTACHED_DEV_VZW_INCOMPATIBLE_MUIC,
+       ATTACHED_DEV_USB_LANHUB_MUIC,
+       ATTACHED_DEV_TYPE1_CHG_MUIC,
+       ATTACHED_DEV_TYPE2_CHG_MUIC,
+       ATTACHED_DEV_UNSUPPORTED_ID_MUIC,
+       ATTACHED_DEV_UNSUPPORTED_ID_VB_MUIC,
+       ATTACHED_DEV_UNDEFINED_RANGE_MUIC,
+       ATTACHED_DEV_RDU_TA_MUIC,
+       ATTACHED_DEV_GAMEPAD_MUIC,
+
+       ATTACHED_DEV_TIMEOUT_OPEN_MUIC,
+       ATTACHED_DEV_HICCUP_MUIC,
+       ATTACHED_DEV_UNKNOWN_MUIC,
+       ATTACHED_DEV_NUM,
+} muic_attached_dev_t;
+
+#ifdef CONFIG_MUIC_HV_FORCE_LIMIT
+/* MUIC attached device type */
+typedef enum {
+       SILENT_CHG_DONE = 0,
+       SILENT_CHG_CHANGING = 1,
+
+       SILENT_CHG_NUM,
+} muic_silent_change_state_t;
+#endif
+
+
+/* muic common callback driver internal data structure
+ * that setted at muic-core.c file
+ */
+struct muic_platform_data {
+       int irq_gpio;
+
+       int switch_sel;
+
+       /* muic current USB/UART path */
+       int usb_path;
+       int uart_path;
+
+       int gpio_uart_sel;
+
+       bool rustproof_on;
+       bool afc_disable;
+
+#ifdef CONFIG_MUIC_HV_FORCE_LIMIT
+       int hv_sel;
+       int silent_chg_change_state;
+#endif
+       enum muic_op_mode opmode;
+
+       /* muic switch dev register function for DockObserver */
+       void (*init_switch_dev_cb)(void);
+       void (*cleanup_switch_dev_cb)(void);
+
+       /* muic GPIO control function */
+       int (*init_gpio_cb)(int switch_sel);
+       int (*set_gpio_usb_sel)(int usb_path);
+       int (*set_gpio_uart_sel)(int uart_path);
+       int (*set_safeout)(int safeout_path);
+
+       /* muic path switch function for rustproof */
+       void (*set_path_switch_suspend)(struct device *dev);
+       void (*set_path_switch_resume)(struct device *dev);
+
+       /* muic AFC voltage switching function */
+       int (*muic_afc_set_voltage_cb)(int voltage);
+
+       /* muic check charger init function */
+       int (*muic_hv_charger_init_cb)(void);
+
+       /* muic set hiccup mode function */
+       int (*muic_set_hiccup_mode_cb)(int on_off);
+};
+
+int get_switch_sel(void);
+int get_afc_mode(void);
+int get_ccic_info(void);
+void muic_set_hmt_status(int status);
+int muic_afc_set_voltage(int voltage);
+int muic_hv_charger_init(void);
+int muic_set_hiccup_mode(int on_off);
+#ifdef CONFIG_SEC_FACTORY
+extern void muic_send_attached_muic_cable_intent(int type);
+#endif /* CONFIG_SEC_FACTORY */
+#endif /* __MUIC_H__ */
diff --git a/include/linux/muic/muic_core.h b/include/linux/muic/muic_core.h
new file mode 100644 (file)
index 0000000..1b4c87f
--- /dev/null
@@ -0,0 +1,418 @@
+/*
+ * include/linux/muic/muic.h
+ *
+ * header file supporting MUIC common information
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ * Seoyoung Jeong <seo0.jeong@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 __MUIC_H__
+#define __MUIC_H__
+
+#ifdef CONFIG_IFCONN_NOTIFIER
+#include <linux/ifconn/ifconn_notifier.h>
+#endif
+
+#define MUIC_CORE "MUIC_CORE"
+/* Status of IF PMIC chip (suspend and resume) */
+enum {
+       MUIC_SUSPEND            = 0,
+       MUIC_RESUME,
+};
+
+/* MUIC Interrupt */
+enum {
+       MUIC_INTR_DETACH        = 0,
+       MUIC_INTR_ATTACH
+};
+
+/* MUIC Dock Observer Callback parameter */
+enum {
+       MUIC_DOCK_DETACHED      = 0,
+       MUIC_DOCK_DESKDOCK      = 1,
+       MUIC_DOCK_CARDOCK       = 2,
+       MUIC_DOCK_AUDIODOCK     = 7,
+       MUIC_DOCK_SMARTDOCK     = 8,
+       MUIC_DOCK_HMT           = 11,
+       MUIC_DOCK_ABNORMAL      = 106,
+       MUIC_DOCK_GAMEPAD       = 107,
+       MUIC_DOCK_GAMEPAD_WITH_EARJACK  = 108,
+};
+
+/* MUIC Path */
+enum {
+       MUIC_PATH_USB_AP        = 0,
+       MUIC_PATH_USB_CP,
+       MUIC_PATH_UART_AP,
+       MUIC_PATH_UART_CP,
+       MUIC_PATH_OPEN,
+       MUIC_PATH_AUDIO,
+};
+
+#ifdef CONFIG_MUIC_HV_FORCE_LIMIT
+enum {
+       HV_9V = 0,
+       HV_5V,
+};
+#endif
+
+/* bootparam SWITCH_SEL */
+enum {
+       SWITCH_SEL_USB_MASK     = 0x1,
+       SWITCH_SEL_UART_MASK    = 0x2,
+       SWITCH_SEL_RUSTPROOF_MASK       = 0x8,
+       SWITCH_SEL_AFC_DISABLE_MASK     = 0x100,
+};
+
+/* bootparam CHARGING_MODE */
+enum {
+       CH_MODE_AFC_DISABLE_VAL = 0x31, /* char '1' */
+};
+
+/* MUIC ADC table */
+typedef enum {
+       ADC_GND                 = 0x00,
+       ADC_SEND_END            = 0x01, /* 0x00001 2K ohm */
+       ADC_REMOTE_S11          = 0x0c, /* 0x01100 20.5K ohm */
+       ADC_REMOTE_S12          = 0x0d, /* 0x01101 24.07K ohm */
+       ADC_RESERVED_VZW        = 0x0e, /* 0x01110 28.7K ohm */
+       ADC_INCOMPATIBLE_VZW    = 0x0f, /* 0x01111 34K ohm */
+       ADC_SMARTDOCK           = 0x10, /* 0x10000 40.2K ohm */
+       ADC_HMT                 = 0x11, /* 0x10001 49.9K ohm */
+       ADC_AUDIODOCK           = 0x12, /* 0x10010 64.9K ohm */
+       ADC_USB_LANHUB          = 0x13, /* 0x10011 80.07K ohm */
+       ADC_CHARGING_CABLE      = 0x14, /* 0x10100 102K ohm */
+       ADC_UNIVERSAL_MMDOCK    = 0x15, /* 0x10101 121K ohm */
+       ADC_UART_CABLE          = 0x16, /* 0x10110 150K ohm */
+       ADC_CEA936ATYPE1_CHG    = 0x17, /* 0x10111 200K ohm */
+       ADC_JIG_USB_OFF         = 0x18, /* 0x11000 255K ohm */
+       ADC_JIG_USB_ON          = 0x19, /* 0x11001 301K ohm */
+       ADC_DESKDOCK            = 0x1a, /* 0x11010 365K ohm */
+       ADC_CEA936ATYPE2_CHG    = 0x1b, /* 0x11011 442K ohm */
+       ADC_JIG_UART_OFF        = 0x1c, /* 0x11100 523K ohm */
+       ADC_JIG_UART_ON         = 0x1d, /* 0x11101 619K ohm */
+       ADC_AUDIOMODE_W_REMOTE  = 0x1e, /* 0x11110 1000K ohm */
+       ADC_OPEN                = 0x1f,
+       ADC_OPEN_219            = 0xfb, /* ADC open or 219.3K ohm */
+       ADC_219                 = 0xfc, /* ADC open or 219.3K ohm */
+
+       ADC_UNDEFINED           = 0xfd, /* Undefied range */
+       ADC_DONTCARE            = 0xfe, /* ADC don't care for MHL */
+       ADC_ERROR               = 0xff, /* ADC value read error */
+} muic_adc_t;
+
+/* MUIC attached device type */
+typedef enum {
+       ATTACHED_DEV_NONE_MUIC = 0,
+
+       ATTACHED_DEV_USB_MUIC,
+       ATTACHED_DEV_CDP_MUIC,
+       ATTACHED_DEV_OTG_MUIC,
+       ATTACHED_DEV_TA_MUIC,
+       ATTACHED_DEV_UNOFFICIAL_MUIC,
+       ATTACHED_DEV_UNOFFICIAL_TA_MUIC,
+       ATTACHED_DEV_UNOFFICIAL_ID_MUIC,
+       ATTACHED_DEV_UNOFFICIAL_ID_TA_MUIC,
+       ATTACHED_DEV_UNOFFICIAL_ID_ANY_MUIC,
+       ATTACHED_DEV_UNOFFICIAL_ID_USB_MUIC,
+
+       ATTACHED_DEV_UNOFFICIAL_ID_CDP_MUIC,
+       ATTACHED_DEV_UNDEFINED_CHARGING_MUIC,
+       ATTACHED_DEV_DESKDOCK_MUIC,
+       ATTACHED_DEV_UNKNOWN_VB_MUIC,
+       ATTACHED_DEV_DESKDOCK_VB_MUIC,
+       ATTACHED_DEV_CARDOCK_MUIC,
+       ATTACHED_DEV_JIG_UART_OFF_MUIC,
+       ATTACHED_DEV_JIG_UART_OFF_VB_MUIC,      /* VBUS enabled */
+       ATTACHED_DEV_JIG_UART_OFF_VB_OTG_MUIC,  /* for otg test */
+       ATTACHED_DEV_JIG_UART_OFF_VB_FG_MUIC,   /* for fuelgauge test */
+
+       ATTACHED_DEV_JIG_UART_ON_MUIC,
+       ATTACHED_DEV_JIG_UART_ON_VB_MUIC,       /* VBUS enabled */
+       ATTACHED_DEV_JIG_USB_OFF_MUIC,
+       ATTACHED_DEV_JIG_USB_ON_MUIC,
+       ATTACHED_DEV_JIG_RID_OPEN_MUIC, /* recovery factory mode 523k to open */
+       ATTACHED_DEV_SMARTDOCK_MUIC,
+       ATTACHED_DEV_SMARTDOCK_VB_MUIC,
+       ATTACHED_DEV_SMARTDOCK_TA_MUIC,
+       ATTACHED_DEV_SMARTDOCK_USB_MUIC,
+       ATTACHED_DEV_UNIVERSAL_MMDOCK_MUIC,
+
+       ATTACHED_DEV_AUDIODOCK_MUIC,
+       ATTACHED_DEV_MHL_MUIC,
+       ATTACHED_DEV_CHARGING_CABLE_MUIC,
+       ATTACHED_DEV_AFC_CHARGER_PREPARE_MUIC,
+       ATTACHED_DEV_AFC_CHARGER_PREPARE_DUPLI_MUIC,
+       ATTACHED_DEV_AFC_CHARGER_5V_MUIC,
+       ATTACHED_DEV_AFC_CHARGER_5V_DUPLI_MUIC,
+       ATTACHED_DEV_AFC_CHARGER_9V_MUIC,
+       ATTACHED_DEV_AFC_CHARGER_ERR_V_MUIC,
+       ATTACHED_DEV_AFC_CHARGER_ERR_V_DUPLI_MUIC,
+
+       ATTACHED_DEV_QC_CHARGER_PREPARE_MUIC,
+       ATTACHED_DEV_QC_CHARGER_5V_MUIC,
+       ATTACHED_DEV_QC_CHARGER_ERR_V_MUIC,
+       ATTACHED_DEV_QC_CHARGER_9V_MUIC,
+       ATTACHED_DEV_HV_ID_ERR_UNDEFINED_MUIC,
+       ATTACHED_DEV_HV_ID_ERR_UNSUPPORTED_MUIC,
+       ATTACHED_DEV_HV_ID_ERR_SUPPORTED_MUIC,
+       ATTACHED_DEV_HMT_MUIC,
+       ATTACHED_DEV_VZW_ACC_MUIC,
+       ATTACHED_DEV_VZW_INCOMPATIBLE_MUIC,
+
+       ATTACHED_DEV_USB_LANHUB_MUIC,
+       ATTACHED_DEV_TYPE2_CHG_MUIC,
+       ATTACHED_DEV_TYPE3_MUIC,
+       ATTACHED_DEV_TYPE3_MUIC_TA,
+       ATTACHED_DEV_TYPE3_ADAPTER_MUIC,
+       ATTACHED_DEV_TYPE3_CHARGER_MUIC,
+       ATTACHED_DEV_NONE_TYPE3_MUIC,
+       ATTACHED_DEV_UNSUPPORTED_ID_MUIC,
+       ATTACHED_DEV_UNSUPPORTED_ID_VB_MUIC,
+       ATTACHED_DEV_TIMEOUT_OPEN_MUIC,
+
+       ATTACHED_DEV_WIRELESS_PAD_MUIC,
+#if defined(CONFIG_SEC_FACTORY)
+       ATTACHED_DEV_CARKIT_MUIC,
+#endif
+       ATTACHED_DEV_POWERPACK_MUIC,
+       ATTACHED_DEV_UNDEFINED_RANGE_MUIC,
+       ATTACHED_DEV_WATER_MUIC,
+       ATTACHED_DEV_CHK_WATER_REQ,
+       ATTACHED_DEV_CHK_WATER_DRY_REQ,
+       ATTACHED_DEV_GAMEPAD_MUIC,
+       ATTACHED_DEV_CHECK_OCP,
+       ATTACHED_DEV_RDU_TA_MUIC,
+
+       ATTACHED_DEV_AFC_CHARGER_9V_DUPLI_MUIC,
+       ATTACHED_DEV_UNKNOWN_MUIC,
+       ATTACHED_DEV_NUM,
+} muic_attached_dev_t;
+
+#ifdef CONFIG_MUIC_HV_FORCE_LIMIT
+/* MUIC attached device type */
+typedef enum {
+       SILENT_CHG_DONE = 0,
+       SILENT_CHG_CHANGING = 1,
+
+       SILENT_CHG_NUM,
+} muic_silent_change_state_t;
+#endif
+
+/* muic common callback driver internal data structure
+ * that setted at muic-core.c file
+ */
+struct muic_platform_data {
+       void *drv_data;
+       void *muic_if;
+       int irq_gpio;
+       bool suspended;
+       bool need_to_noti;
+
+       int switch_sel;
+
+       /* muic current USB/UART path */
+       int usb_path;
+       int uart_path;
+
+       int gpio_uart_sel;
+       int gpio_usb_sel;
+
+       bool rustproof_on;
+       bool afc_disable;
+
+#ifdef CONFIG_MUIC_HV_FORCE_LIMIT
+       int hv_sel;
+       int silent_chg_change_state;
+#endif
+
+       /* muic current attached device */
+       muic_attached_dev_t     attached_dev;
+
+       bool is_usb_ready;
+       bool is_factory_start;
+       bool is_rustproof;
+       bool is_otg_test;
+
+       bool is_jig_on;
+
+       int vbvolt;
+       int adc;
+       int vmid;
+
+       /* muic switch dev register function for DockObserver */
+       void (*init_switch_dev_cb)(void);
+       void (*cleanup_switch_dev_cb)(void);
+
+       void (*jig_uart_cb)(int jig_state);
+
+       /* muic GPIO control function */
+       int (*init_gpio_cb)(void *, int switch_sel);
+       int (*set_gpio_usb_sel)(int usb_path);
+       int (*set_gpio_uart_sel)(int uart_path);
+       int (*set_safeout)(int safeout_path);
+
+       /* muic path switch function for rustproof */
+       void (*set_path_switch_suspend)(struct device *dev);
+       void (*set_path_switch_resume)(struct device *dev);
+};
+
+#define MUIC_PDATA_FUNC(func, param, ret) \
+{\
+       *ret = -1;      \
+       if (func)       \
+               *ret = func(param);     \
+       else    \
+               pr_err("[muic_core] func not defined %s\n", __func__);  \
+}
+
+#define MUIC_PDATA_FUNC_MULTI_PARAM(func, param1, param2, ret) \
+{                                      \
+       *ret = -1;      \
+       if (func)       \
+               *ret = func(param1, param2);    \
+       else    \
+               pr_err("[muic_core] func not defined %s\n", __func__);  \
+}
+
+#ifdef CONFIG_IFCONN_NOTIFIER
+#define MUIC_SEND_NOTI_ATTACH(dev) \
+{      \
+       int ret;        \
+       ret = ifconn_notifier_notify( \
+                                       IFCONN_NOTIFY_MUIC,     \
+                                       IFCONN_NOTIFY_MANAGER,  \
+                                       IFCONN_NOTIFY_ID_ATTACH,        \
+                                       dev,    \
+                                       IFCONN_NOTIFY_PARAM_DATA,       \
+                                       NULL);  \
+       if (ret < 0) {  \
+               pr_err("%s: Fail to send noti\n", \
+                               __func__);      \
+       }       \
+}
+
+#define MUIC_SEND_NOTI_ATTACH_ALL(dev) \
+{      \
+       int ret;        \
+       ret = ifconn_notifier_notify( \
+                                       IFCONN_NOTIFY_MUIC,     \
+                                       IFCONN_NOTIFY_ALL,      \
+                                       IFCONN_NOTIFY_ID_ATTACH,        \
+                                       dev,    \
+                                       IFCONN_NOTIFY_PARAM_DATA,       \
+                                       NULL);  \
+       if (ret < 0) {  \
+               pr_err("%s: Fail to send noti\n", \
+                               __func__);      \
+       }       \
+}
+
+#define MUIC_SEND_NOTI_DETACH_ALL(dev) \
+{      \
+       int ret;        \
+       ret = ifconn_notifier_notify( \
+                                       IFCONN_NOTIFY_MUIC,     \
+                                       IFCONN_NOTIFY_ALL,      \
+                                       IFCONN_NOTIFY_ID_DETACH,        \
+                                       dev,    \
+                                       IFCONN_NOTIFY_PARAM_DATA,       \
+                                       NULL);  \
+       if (ret < 0) {  \
+               pr_err("%s: Fail to send noti\n", \
+                               __func__);      \
+       }       \
+}
+
+#define MUIC_SEND_NOTI_TO_CCIC_ATTACH(dev) \
+{      \
+       int ret;        \
+       struct ifconn_notifier_template template;       \
+       template.cable_type = dev;      \
+       ret = ifconn_notifier_notify( \
+                                       IFCONN_NOTIFY_MUIC,     \
+                                       IFCONN_NOTIFY_CCIC,     \
+                                       IFCONN_NOTIFY_ID_ATTACH,        \
+                                       IFCONN_NOTIFY_EVENT_ATTACH,     \
+                                       IFCONN_NOTIFY_PARAM_DATA,       \
+                                       &template);     \
+       if (ret < 0) {  \
+               pr_err("%s: Fail to send noti\n", \
+                               __func__);      \
+       }       \
+}
+
+#define MUIC_SEND_NOTI_TO_CCIC_DETACH(dev) \
+{      \
+       int ret;        \
+       struct ifconn_notifier_template template;       \
+       template.cable_type = dev;      \
+       ret = ifconn_notifier_notify( \
+                                       IFCONN_NOTIFY_MUIC,     \
+                                       IFCONN_NOTIFY_CCIC,     \
+                                       IFCONN_NOTIFY_ID_DETACH,        \
+                                       IFCONN_NOTIFY_EVENT_DETACH,     \
+                                       IFCONN_NOTIFY_PARAM_DATA,       \
+                                       &template);     \
+       if (ret < 0) {  \
+               pr_err("%s: Fail to send noti\n", \
+                               __func__);      \
+       }       \
+}
+
+#define MUIC_SEND_NOTI_DETACH(dev) \
+{      \
+       int ret;        \
+       struct ifconn_notifier_template template;       \
+       template.cable_type = dev;      \
+       ret = ifconn_notifier_notify( \
+                                       IFCONN_NOTIFY_MUIC,     \
+                                       IFCONN_NOTIFY_MANAGER,  \
+                                       IFCONN_NOTIFY_ID_DETACH,        \
+                                       IFCONN_NOTIFY_EVENT_DETACH,     \
+                                       IFCONN_NOTIFY_PARAM_DATA,       \
+                                       &template);     \
+       if (ret < 0) {  \
+               pr_err("%s: Fail to send noti\n", \
+                               __func__);      \
+       }       \
+}
+#else
+#define MUIC_SEND_NOTI_ATTACH(dev)     \
+               muic_notifier_attach_attached_dev(dev)
+#define MUIC_SEND_NOTI_DETACH(dev) \
+               muic_notifier_detach_attached_dev(dev)
+#define MUIC_SEND_NOTI_TO_CCIC_ATTACH(dev) \
+               muic_pdic_notifier_attach_attached_dev(dev)
+#define MUIC_SEND_NOTI_TO_CCIC_DETACH(dev) \
+               muic_pdic_notifier_detach_attached_dev(dev)
+#endif
+
+int get_switch_sel(void);
+int get_afc_mode(void);
+void muic_set_hmt_status(int status);
+int muic_core_handle_attach(struct muic_platform_data *muic_pdata,
+                       muic_attached_dev_t new_dev, int adc, u8 vbvolt);
+int muic_core_handle_detach(struct muic_platform_data *muic_pdata);
+bool muic_core_get_ccic_cable_state(struct muic_platform_data *muic_pdata);
+struct muic_platform_data *muic_core_init(void *drv_data);
+void muic_core_exit(struct muic_platform_data *muic_pdata);
+extern void muic_disable_otg_detect(void);
+bool muic_core_is_cable_attached(struct muic_platform_data *muic_pdata);
+#endif /* __MUIC_H__ */
diff --git a/include/linux/muic/muic_interface.h b/include/linux/muic/muic_interface.h
new file mode 100644 (file)
index 0000000..7e6a178
--- /dev/null
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2010 Samsung Electronics
+ * Thomas Ryu <smilesr.ryu@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 __MUIC_INTERNAL_H__
+#define __MUIC_INTERNAL_H__
+
+#include <linux/muic/muic_core.h>
+
+#define muic_err(fmt, ...)                                     \
+       do {                                                    \
+               pr_err(pr_fmt(fmt), ##__VA_ARGS__);             \
+       } while (0)
+
+#define muic_info(fmt, ...)                                    \
+       do {                                                    \
+               pr_info(pr_fmt(fmt), ##__VA_ARGS__);            \
+       } while (0)
+
+#define muic_dbg(fmt, ...)                                     \
+       do {                                                    \
+               pr_debug(pr_fmt(fmt), ##__VA_ARGS__);           \
+       } while (0)
+
+enum muic_op_mode {
+       OPMODE_SMD_ARRAY = 0<<0,
+       OPMODE_DEVICE = 1<<0,
+};
+
+/* Slave addr = 0x4A: MUIC */
+enum ioctl_cmd {
+       GET_COM_VAL = 0x01,
+       GET_CTLREG = 0x02,
+       GET_ADC = 0x03,
+       GET_SWITCHING_MODE = 0x04,
+       GET_INT_MASK = 0x05,
+       GET_REVISION = 0x06,
+       GET_OTG_STATUS = 0x7,
+       GET_CHGTYPE = 0x08,
+       GET_RESID3 = 0x09,
+};
+
+enum switching_mode {
+       SWMODE_MANUAL = 0,
+       SWMODE_AUTO = 1,
+};
+
+/*
+ * Manual Switch
+ * D- [7:5] / D+ [4:2] / Vbus [1:0]
+ * 000: Open all / 001: USB / 010: AUDIO / 011: UART / 100: V_AUDIO
+ * 00: Vbus to Open / 01: Vbus to Charger / 10: Vbus to MIC / 11: Vbus to VBout
+ */
+
+/* COM port index */
+enum com_index {
+       COM_OPEN = 1,
+       COM_OPEN_WITH_V_BUS = 2,
+       COM_UART_AP = 3,
+       COM_UART_CP = 4,
+       COM_USB_AP  = 5,
+       COM_USB_CP  = 6,
+       COM_AUDIO   = 7,
+};
+
+enum {
+       ADC_SCANMODE_CONTINUOUS = 0x0,
+       ADC_SCANMODE_ONESHOT = 0x1,
+       ADC_SCANMODE_PULSE = 0x2,
+};
+
+enum vps_type {
+       VPS_TYPE_SCATTERED = 0,
+       VPS_TYPE_TABLE = 1,
+};
+
+/* VPS data from a chip. */
+typedef struct _muic_vps_scatterred_type {
+       u8 val1;
+       u8 val2;
+       u8 val3;
+       u8 adc;
+       u8 vbvolt;
+} vps_scatterred_type;
+
+typedef struct _muic_vps_table_t {
+       u8  adc;
+       u8  vbvolt;
+       u8  adc1k;
+       u8  adcerr;
+       u8  adclow;
+       u8  chgdetrun;
+       u8  chgtyp;
+       u8  DCDTimedout;
+       const char *vps_name;
+       const muic_attached_dev_t attached_dev;
+       u8 status[3];
+       u8 control[4];
+       u8 hvcontrol[2];
+} vps_table_type;
+
+struct muic_intr_data {
+       u8      intr1;
+       u8      intr2;
+};
+
+struct muic_irq_t {
+       int irq_adc1k;
+       int irq_adcerr;
+       int irq_adc;
+       int irq_chgtyp;
+       int irq_vbvolt;
+       int irq_dcdtmr;
+};
+
+typedef union _muic_vps_t {
+       vps_scatterred_type s;
+       vps_table_type t;
+       char vps_data[120];
+} vps_data_t;
+
+/* muic chip specific internal data structure
+ * that setted at muic-xxxx.c file
+ */
+struct regmap_desc;
+
+typedef enum {
+       CCIC_RID_UNDEFINED = 0,
+       CCIC_RID_000K,
+       CCIC_RID_001K,
+       CCIC_RID_255K,
+       CCIC_RID_301K,
+       CCIC_RID_523K,
+       CCIC_RID_619K,
+       CCIC_RID_OPEN,
+} muic_ccic_rid_t;
+
+struct ccic_rid_desc_t {
+       char *name;
+       int attached_dev;
+};
+
+struct ccic_desc_t {
+       int ccic_evt_attached; /* 1: attached, -1: detached, 0: undefined */
+       int ccic_evt_rid; /* the last rid */
+       int ccic_evt_rprd; /*rprd */
+       int ccic_evt_roleswap; /* check rprd role swap event */
+       int ccic_evt_dcdcnt; /* count dcd timeout */
+       int attached_dev; /* attached dev */
+       struct ccic_rid_desc_t *rid_desc;
+};
+
+enum {
+       MUIC_NORMAL_OTG,
+       MUIC_ABNORMAL_OTG,
+};
+
+struct muic_interface_t {
+       struct device *dev;
+       struct i2c_client *i2c; /* i2c addr: 0x4A; MUIC */
+       struct mutex muic_mutex;
+       struct notifier_block nb;
+
+       /* model dependant muic platform data */
+       struct muic_platform_data *pdata;
+       struct ccic_desc_t *ccic;
+
+       void *muic_data;
+
+       /* muic current attached device */
+       muic_attached_dev_t attached_dev;
+
+       vps_data_t vps;
+       int vps_table;
+
+       struct muic_intr_data intr;
+       struct muic_irq_t irqs;
+
+       /* regmap_desc_t */
+       struct regmap_desc *regmapdesc;
+
+       char *chip_name;
+
+       int gpio_uart_sel;
+       int gpio_usb_sel;
+
+       /* muic Device ID */
+       u8 muic_vendor;                 /* Vendor ID */
+       u8 muic_version;                /* Version ID */
+
+       bool                    is_gamepad;
+       bool                    is_factory_start;
+       bool                    is_rustproof;
+       bool                    is_otg_test;
+       struct delayed_work     init_work;
+       struct delayed_work     usb_work;
+
+       bool                    is_muic_ready;
+       bool                    undefined_range;
+       bool                    discard_interrupt;
+       bool                    is_dcdtmr_intr;
+       bool                    is_dcp_charger;
+       bool                    is_afc_reset;
+
+       struct hv_data          *phv;
+
+#if defined(CONFIG_USB_EXTERNAL_NOTIFY)
+       /* USB Notifier */
+       struct notifier_block   usb_nb;
+#endif
+
+#if defined(CONFIG_MUIC_MANAGER)
+       /* legacy TA or USB for CCIC */
+       muic_attached_dev_t     legacy_dev;
+
+#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER
+       struct notifier_block   manager_nb;
+#else
+       struct notifier_block   ccic_nb;
+#endif
+       struct delayed_work     ccic_work;
+       bool afc_water_disable;
+#endif
+       /* Operation Mode */
+       enum muic_op_mode       opmode;
+
+       /* function pointer should be registered from each specific driver file */
+       int (*set_com_to_open)(void *);
+       int (*set_switch_to_usb)(void *);
+       int (*set_switch_to_uart)(void *);
+       void (*set_jig_state)(void *, bool val);
+       int (*set_jig_ctrl_on)(void *);
+       void (*set_cable_state)(void *, muic_attached_dev_t new_dev);
+       void (*set_otg_detect_en)(void *, bool en);
+       void (*set_dcd_rescan)(void *);
+       void (*set_afc_ready)(void *, bool en);
+       int (*bcd_rescan)(void *);
+       int (*control_rid_adc)(void *, bool enable);
+#if defined(CONFIG_MUIC_S2MU004_HV)
+       int (*set_afc_reset)(void *);
+       muic_attached_dev_t (*check_id_err)(void *, muic_attached_dev_t new_dev);
+       int (*reset_hvcontrol_reg)(void *);
+       int (*check_afc_ready)(void *);
+       int (*reset_afc_register)(void *);
+#endif
+       void (*set_water_detect)(void *, bool val);
+       int (*set_com_to_audio)(void *);
+       int (*set_com_to_otg)(void *);
+       int (*set_gpio_usb_sel)(void *, int usb_path);
+       int (*set_gpio_uart_sel)(void *, int uart_path);
+       int (*get_vbus)(void *);
+       int (*get_adc)(void *);
+};
+
+extern struct device *switch_device;
+
+struct muic_interface_t *muic_manager_init(void *pdata, void *drv_data);
+void muic_manager_exit(struct muic_interface_t *muic_if);
+int muic_manager_get_legacy_dev(struct muic_interface_t *muic_if);
+void muic_manager_set_legacy_dev(struct muic_interface_t *muic_if, int new_dev);
+void muic_manager_handle_ccic_detach(struct muic_interface_t *muic_if);
+int muic_manager_dcd_rescan(struct muic_interface_t *muic_if);
+
+#endif /* __MUIC_INTERNAL_H__ */
diff --git a/include/linux/muic/muic_notifier.h b/include/linux/muic/muic_notifier.h
new file mode 100644 (file)
index 0000000..902e8c2
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * include/linux/muic/muic_notifier.h
+ *
+ * header file supporting MUIC 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 __MUIC_NOTIFIER_H__
+#define __MUIC_NOTIFIER_H__
+
+#include <linux/ccic/ccic_notifier.h>
+
+/* MUIC notifier call chain command */
+typedef enum {
+       MUIC_NOTIFY_CMD_DETACH  = 0,
+       MUIC_NOTIFY_CMD_ATTACH,
+       MUIC_NOTIFY_CMD_LOGICALLY_DETACH,
+       MUIC_NOTIFY_CMD_LOGICALLY_ATTACH,
+} muic_notifier_cmd_t;
+
+/* MUIC notifier call sequence,
+ * largest priority number device will be called first. */
+typedef enum {
+       MUIC_NOTIFY_DEV_DOCK = 0,
+       MUIC_NOTIFY_DEV_MHL,
+       MUIC_NOTIFY_DEV_USB,
+       MUIC_NOTIFY_DEV_TSP,
+       MUIC_NOTIFY_DEV_CHARGER,
+       MUIC_NOTIFY_DEV_CPUIDLE,
+       MUIC_NOTIFY_DEV_CPUFREQ,
+#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER
+       MUIC_NOTIFY_DEV_MANAGER,
+#endif
+       MUIC_NOTIFY_DEV_CABLE_DATA,
+} muic_notifier_device_t;
+
+struct muic_notifier_struct {
+       muic_attached_dev_t attached_dev;
+       muic_notifier_cmd_t cmd;
+       CC_NOTI_ATTACH_TYPEDEF cxt;
+       struct blocking_notifier_head notifier_call_chain;
+};
+
+#define MUIC_NOTIFIER_BLOCK(name)      \
+       struct notifier_block (name)
+
+/* muic notifier init/notify function
+ * this function is for JUST MUIC device driver.
+ * DON'T use function anywhrer else!!
+ */
+extern void muic_notifier_attach_attached_dev(muic_attached_dev_t new_dev);
+extern void muic_notifier_detach_attached_dev(muic_attached_dev_t cur_dev);
+extern void muic_notifier_logically_attach_attached_dev(muic_attached_dev_t new_dev);
+extern void muic_notifier_logically_detach_attached_dev(muic_attached_dev_t cur_dev);
+
+/* muic notifier register/unregister API
+ * for used any where want to receive muic attached device attach/detach. */
+extern int muic_notifier_register(struct notifier_block *nb,
+               notifier_fn_t notifier, muic_notifier_device_t listener);
+extern int muic_notifier_unregister(struct notifier_block *nb);
+
+/* Choose a proper noti. interface for a test */
+extern void muic_notifier_set_new_noti(bool flag);
+
+#endif /* __MUIC_NOTIFIER_H__ */
diff --git a/include/linux/muic/muic_sysfs.h b/include/linux/muic/muic_sysfs.h
new file mode 100644 (file)
index 0000000..073eb22
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef MUIC_SYSFS_H
+#define MUIC_SYSFS_H
+
+#ifdef CONFIG_MUIC_SYSFS
+extern struct device *muic_device_create(void *drvdata, const char *fmt);
+extern void muic_device_destroy(dev_t devt);
+#else
+static inline struct device *muic_device_create(void *drvdata, const char *fmt)
+{
+       pr_err("No rule to make muic sysfs\n");
+       return NULL;
+}
+static inline void muic_device_destroy(dev_t devt)
+{
+       return;
+}
+#endif
+
+#endif /* MUIC_SYSFS_H */
diff --git a/include/linux/muic/s2mu004-muic-hv.h b/include/linux/muic/s2mu004-muic-hv.h
new file mode 100644 (file)
index 0000000..d754e1e
--- /dev/null
@@ -0,0 +1,291 @@
+/*
+ * s2mu004-muic-hv.h - MUIC for the Samsung s2mu004
+ *
+ * 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
+ *
+ * This driver is based on max77843-muic.h
+ *
+ */
+
+#ifndef __S2MU004_MUIC_HV_H__
+#define __S2MU004_MUIC_HV_H__
+
+#define MUIC_HV_DEV_NAME               "muic-s2mu004-hv"
+
+enum s2mu004_reg_bit_control {
+       S2MU004_DISABLE_BIT             = 0,
+       S2MU004_ENABLE_BIT,
+};
+
+/* S2MU004 AFC INTMASK register */
+#define INTMASK_VBADCM_SHIFT           0
+#define INTMASK_VDNMONM_SHIFT          1
+#define INTMASK_DNRESM_SHIFT           2
+#define INTMASK_MPNACKM_SHIFT          3
+#define INTMASK_MRXBUFOWM_SHIFT        4
+#define INTMASK_MRXTRFM_SHIFT          5
+#define INTMASK_MRXPERRM_SHIFT         6
+#define INTMASK_MRXRDYM_SHIFT          7
+#define INTMASK_VBADCM_MASK            (S2MU004_ENABLE_BIT << INTMASK_VBADCM_SHIFT)
+#define INTMASK_VDNMONM_MASK           (S2MU004_ENABLE_BIT << INTMASK_VDNMONM_SHIFT)
+#define INTMASK_DNRESM_MASK            (S2MU004_ENABLE_BIT << INTMASK_DNRESM_SHIFT)
+#define INTMASK_MPNACKM_MASK           (S2MU004_ENABLE_BIT << INTMASK_MPNACKM_SHIFT)
+#define INTMASK_MRXBUFOWM_MASK         (S2MU004_ENABLE_BIT << INTMASK_MRXBUFOWM_SHIFT)
+#define INTMASK_MRXTRFM_MASK           (S2MU004_ENABLE_BIT << INTMASK_MRXTRFM_SHIFT)
+#define INTMASK_MRXPERRM_MASK          (S2MU004_ENABLE_BIT << INTMASK_MRXPERRM_SHIFT)
+#define INTMASK_MRXRDYM_MASK           (S2MU004_ENABLE_BIT << INTMASK_MRXRDYM_SHIFT)
+
+/* S2MU004 AFC STATUS register */
+#define STATUS_VBADC_SHIFT             0
+#define STATUS_VDNMON_SHIFT            4
+#define STATUS_DNRES_SHIFT             5
+#define STATUS_MPNACK_SHIFT            6
+#define STATUS_VBADC_MASK              (0xf << STATUS_VBADC_SHIFT)
+#define STATUS_VDNMON_MASK             (0x1 << STATUS_VDNMON_SHIFT)
+#define STATUS_DNRES_MASK              (0x1 << STATUS_DNRES_SHIFT)
+#define STATUS_MPNACK_MASK             (0x1 << STATUS_MPNACK_SHIFT)
+
+/* S2MU004 HVCONTROL1 register */
+#define HVCONTROL1_DPDNVDEN_SHIFT      0
+#define HVCONTROL1_DNVD_SHIFT          1
+#define HVCONTROL1_DPVD_SHIFT          3
+#define HVCONTROL1_VBUSADCEN_SHIFT     5
+#define HVCONTROL1_CTRLIDMON_SHIFT     6
+#define HVCONTROL1_AFCEN_SHIFT 7
+#define HVCONTROL1_DPDNVDEN_MASK       (0x1 << HVCONTROL1_DPDNVDEN_SHIFT)
+#define HVCONTROL1_DNVD_MASK           (0x3 << HVCONTROL1_DNVD_SHIFT)
+#define HVCONTROL1_DPVD_MASK           (0x3 << HVCONTROL1_DPVD_SHIFT)
+#define HVCONTROL1_VBUSADCEN_MASK      (0x1 << HVCONTROL1_VBUSADCEN_SHIFT)
+#define HVCONTROL1_CTRLIDMON_MASK      (0x1 << HVCONTROL1_CTRLIDMON_SHIFT)
+#define HVCONTROL1_AFCEN_MASK  (0x1 << HVCONTROL1_AFCEN_SHIFT)
+
+/* S2MU004 HVCONTROL2 register */
+//#define HVCONTROL2_HVDIGEN_SHIFT     0
+#define HVCONTROL2_DP06EN_SHIFT                1
+#define HVCONTROL2_DNRESEN_SHIFT       2
+#define HVCONTROL2_MPING_SHIFT         3
+#define HVCONTROL2_MTXEN_SHIFT         4
+#define HVCONTROL2_RSTDM100UI_SHIFT            7
+//#define HVCONTROL2_MTXBUSRES_SHIFT   5
+//#define HVCONTROL2_MPNGENB_SHIFT     6
+//#define HVCONTROL2_HVDIGEN_MASK              (0x1 << HVCONTROL2_HVDIGEN_SHIFT)
+#define HVCONTROL2_DP06EN_MASK         (0x1 << HVCONTROL2_DP06EN_SHIFT)
+#define HVCONTROL2_DNRESEN_MASK                (0x1 << HVCONTROL2_DNRESEN_SHIFT)
+#define HVCONTROL2_MPING_MASK          (0x1 << HVCONTROL2_MPING_SHIFT)
+#define HVCONTROL2_MTXEN_MASK          (0x1 << HVCONTROL2_MTXEN_SHIFT)
+#define HVCONTROL2_RSTDM100UI_MASK             (0x1 << HVCONTROL2_RSTDM100UI_SHIFT)
+//#define HVCONTROL2_MTXBUSRES_MASK    (0x1 << HVCONTROL2_MTXBUSRES_SHIFT)
+//#define HVCONTROL2_MPNGENB_MASK              (0x1 << HVCONTROL2_MPNGENB_SHIFT)
+
+#define HVTXBYTE_5V 0x0
+#define HVTXBYTE_6V 0x1
+#define HVTXBYTE_7V 0x2
+#define HVTXBYTE_8V 0x3
+#define HVTXBYTE_9V 0x4
+#define HVTXBYTE_10V 0x5
+#define HVTXBYTE_11V 0x6
+#define HVTXBYTE_12V 0x7
+#define HVTXBYTE_13V 0x8
+#define HVTXBYTE_14V 0x9
+#define HVTXBYTE_15V 0xA
+#define HVTXBYTE_16V 0xB
+#define HVTXBYTE_17V 0xC
+#define HVTXBYTE_18V 0xD
+#define HVTXBYTE_19V 0xE
+#define HVTXBYTE_20V 0xF
+
+#define HVTXBYTE_0_75A 0x0
+#define HVTXBYTE_0_90A 0x1
+#define HVTXBYTE_1_05A 0x2
+#define HVTXBYTE_1_20A 0x3
+#define HVTXBYTE_1_35A 0x4
+#define HVTXBYTE_1_50A 0x5
+#define HVTXBYTE_1_65A 0x6
+#define HVTXBYTE_1_80A 0x7
+#define HVTXBYTE_1_95A 0x8
+#define HVTXBYTE_2_10A 0x9
+#define HVTXBYTE_2_25A 0xA
+#define HVTXBYTE_2_40A 0xB
+#define HVTXBYTE_2_55A 0xC
+#define HVTXBYTE_2_70A 0xD
+#define HVTXBYTE_2_85A 0xE
+#define HVTXBYTE_3_00A 0xF
+
+/* S2MU004 HVRXBYTE register */
+#define HVRXBYTE_MAX                   16
+
+/* S2MU004 AFC charger W/A Check NUM */
+#define AFC_CHARGER_WA_PING            5
+
+/* S2MU004 MPing miss SW Workaround - delay time */
+#define MPING_MISS_WA_TIME             2000
+
+#define MUIC_HV_5V     0x08
+#define MUIC_HV_9V     0x46
+
+#if 0
+typedef enum {
+       DPDNVDEN_DISABLE        = 0x00,
+       DPDNVDEN_ENABLE         = (0x1 << HVCONTROL1_DPDNVDEN_SHIFT),
+
+       DPDNVDEN_DONTCARE       = 0xff,
+} dpdnvden_t;
+#endif
+
+typedef enum {
+       VDNMON_LOW              = 0x00,
+       VDNMON_HIGH             = (0x1 << STATUS_VDNMON_SHIFT),
+
+       VDNMON_DONTCARE         = 0xff,
+} vdnmon_t;
+
+typedef enum {
+//     VBADC_VBDET             = 0x00,
+       VBADC_5_3V              = 0x00,
+       VBADC_5_7V_6_3V         = (0x1 << STATUS_VBADC_SHIFT),
+       VBADC_6_7V_7_3V         = (0x2 << STATUS_VBADC_SHIFT),
+       VBADC_7_7V_8_3V         = (0x3 << STATUS_VBADC_SHIFT),
+       VBADC_8_7V_9_3V         = (0x4 << STATUS_VBADC_SHIFT),
+       VBADC_9_7V_10_3V                = (0x5 << STATUS_VBADC_SHIFT),
+       VBADC_10_7V_11_3V               = (0x6 << STATUS_VBADC_SHIFT),
+       VBADC_11_7V_12_3V               = (0x7 << STATUS_VBADC_SHIFT),
+       VBADC_12_7V_13_3V               = (0x8 << STATUS_VBADC_SHIFT),
+       VBADC_13_7V_14_3V               = (0x9 << STATUS_VBADC_SHIFT),
+       VBADC_14_7V_15_3V               = (0xA << STATUS_VBADC_SHIFT),
+       VBADC_15_7V_16_3V               = (0xB << STATUS_VBADC_SHIFT),
+       VBADC_16_7V_17_3V               = (0xC << STATUS_VBADC_SHIFT),
+       VBADC_17_7V_18_3V               = (0xD << STATUS_VBADC_SHIFT),
+       VBADC_18_7V_19_3V               = (0xE << STATUS_VBADC_SHIFT),
+       VBADC_19_7V             = (0xF << STATUS_VBADC_SHIFT),
+
+//     VBADC_QC_5V             = 0xeb,
+//     VBADC_QC_9V             = 0xec,
+//     VBADC_QC_12V            = 0xed,
+//     VBADC_QC_20V            = 0xee,
+
+       VBADC_AFC_5V            = 0xfa,
+       VBADC_AFC_9V            = 0xfb,
+       VBADC_QC_9V             = 0xfb,
+       VBADC_AFC_ERR_V         = 0xfc,
+       VBADC_AFC_ERR_V_NOT_0   = 0xfd,
+
+       VBADC_ANY               = 0xfe,
+       VBADC_DONTCARE          = 0xff,
+} vbadc_t;
+
+#if 0
+enum {
+       HV_SUPPORT_QC_5V        = 5,
+       HV_SUPPORT_QC_9V        = 9,
+       HV_SUPPORT_QC_12V       = 12,
+       HV_SUPPORT_QC_20V       = 20,
+};
+#endif
+
+/* MUIC afc irq type */
+typedef enum {
+       MUIC_AFC_IRQ_VDNMON = 0,
+       MUIC_AFC_IRQ_MRXRDY,
+       MUIC_AFC_IRQ_VBADC,
+       MUIC_AFC_IRQ_MPNACK,
+       MUIC_AFC_IRQ_DONTCARE = 0xff,
+} muic_afc_irq_t;
+
+/* muic chip specific internal data structure */
+typedef struct s2mu004_muic_afc_data {
+       muic_attached_dev_t             new_dev;
+       const char                      *afc_name;
+       muic_afc_irq_t                  afc_irq;
+//     u8                              hvcontrol1_dpdnvden;
+       u8                              status_vbadc;
+       u8                              status_vdnmon;
+       int                             function_num;
+       struct s2mu004_muic_afc_data    *next;
+} muic_afc_data_t;
+
+enum s2mu004_reg_hv_val {
+#if 0
+       S2MU004_MUIC_HVCONTROL1_DPVD_06 = (0x2 << HVCONTROL1_DPVD_SHIFT),
+       S2MU004_MUIC_HVCONTROL1_11      = (S2MU004_MUIC_HVCONTROL1_DPVD_06 |
+                                       HVCONTROL1_DPDNVDEN_MASK),
+       S2MU004_MUIC_HVCONTROL1_31      = (HVCONTROL1_VBUSADCEN_MASK |
+                                       S2MU004_MUIC_HVCONTROL1_DPVD_06 |
+                                       HVCONTROL1_DPDNVDEN_MASK),
+       S2MU004_MUIC_HVCONTROL2_06      = (HVCONTROL2_DP06EN_MASK | HVCONTROL2_DNRESEN_MASK),
+       S2MU004_MUIC_HVCONTROL2_13      = (HVCONTROL2_MTXEN_MASK | HVCONTROL2_DP06EN_MASK |
+                                       HVCONTROL2_HVDIGEN_MASK),
+       S2MU004_MUIC_HVCONTROL2_1B      = (HVCONTROL2_HVDIGEN_MASK | HVCONTROL2_DP06EN_MASK |
+                                       HVCONTROL2_MPING_MASK | HVCONTROL2_MTXEN_MASK),
+       S2MU004_MUIC_HVCONTROL2_1F      = (HVCONTROL2_HVDIGEN_MASK | HVCONTROL2_DP06EN_MASK |
+                                       HVCONTROL2_DNRESEN_MASK | HVCONTROL2_MPING_MASK | HVCONTROL2_MTXEN_MASK),
+       S2MU004_MUIC_HVCONTROL2_5B      = (HVCONTROL2_HVDIGEN_MASK | HVCONTROL2_DP06EN_MASK |
+                                       HVCONTROL2_MPING_MASK | HVCONTROL2_MTXEN_MASK | HVCONTROL2_MPNGENB_MASK),
+       S2MU004_MUIC_INTMASK3_FB        = (INTMASK3_MRXRDYM_MASK | INTMASK3_MRXPERRM_MASK |
+                                       INTMASK3_MRXTRFM_MASK | INTMASK3_MRXBUFOWM_MASK |
+                                       INTMASK3_MPNACKM_MASK | INTMASK3_VDNMONM_MASK |
+                                       INTMASK3_VBADCM_MASK),
+#endif
+       S2MU004_MUIC_HVCONTROL1_A0      = (HVCONTROL1_AFCEN_MASK |
+                                       HVCONTROL1_VBUSADCEN_MASK),
+
+       S2MU004_MUIC_HVCONTROL2_06      = (HVCONTROL2_DNRESEN_MASK |
+                                       HVCONTROL2_DP06EN_MASK),
+
+       S2MU004_MUIC_HVCONTROL1_E0      = (HVCONTROL1_AFCEN_MASK | HVCONTROL1_CTRLIDMON_MASK |
+                                       HVCONTROL1_VBUSADCEN_MASK),
+
+       S2MU004_MUIC_HVCONTROL2_0E      = (HVCONTROL2_MPING_MASK | HVCONTROL2_DNRESEN_MASK |
+                                       HVCONTROL2_DP06EN_MASK),
+};
+
+struct s2mu004_muic_data;
+extern int s2mu004_muic_reset_afc_register(struct s2mu004_muic_data *muic_data);
+extern bool muic_check_dev_ta(struct s2mu004_muic_data *muic_data);
+extern bool muic_check_is_hv_dev(struct s2mu004_muic_data *muic_data);
+extern muic_attached_dev_t hv_muic_check_id_err
+       (struct s2mu004_muic_data *muic_data, muic_attached_dev_t new_dev);
+extern void s2mu004_hv_muic_reset_hvcontrol_reg(struct s2mu004_muic_data *muic_data);
+#if defined(CONFIG_OF)
+extern int of_s2mu004_hv_muic_dt(struct s2mu004_muic_data *muic_data);
+#endif
+
+extern int s2mu004_afc_muic_irq_init(struct s2mu004_muic_data *muic_data);
+extern void s2mu004_hv_muic_free_irqs(struct s2mu004_muic_data *muic_data);
+
+extern int s2mu004_muic_hv_update_reg(struct i2c_client *i2c,
+               const u8 reg, const u8 val, const u8 mask, const bool debug_en);
+extern void s2mu004_muic_set_afc_ready(struct s2mu004_muic_data *muic_data, bool value);
+
+extern void s2mu004_hv_muic_init_check_dpdnvden(struct s2mu004_muic_data *muic_data);
+extern void s2mu004_hv_muic_init_detect(struct s2mu004_muic_data *muic_data);
+extern void s2mu004_hv_muic_initialize(struct s2mu004_muic_data *muic_data);
+extern void s2mu004_hv_muic_remove(struct s2mu004_muic_data *muic_data);
+//extern void s2mu004_hv_muic_remove_wo_free_irq(struct s2mu004_muic_data *muic_data);
+#if 0
+extern void s2mu004_muic_set_adcmode_always(struct s2mu004_muic_data *muic_data);
+#if !defined(CONFIG_SEC_FACTORY)
+extern void s2mu004_muic_set_adcmode_oneshot(struct s2mu004_muic_data *muic_data);
+#endif /* !CONFIG_SEC_FACTORY */
+extern void s2mu004_hv_muic_adcmode_oneshot(struct s2mu004_muic_data *muic_data);
+#endif
+extern void s2mu004_muic_prepare_afc_charger(struct s2mu004_muic_data *muic_data);
+extern bool s2mu004_muic_check_change_dev_afc_charger
+       (struct s2mu004_muic_data *muic_data, muic_attached_dev_t new_dev);
+
+#endif /* __S2MU004_MUIC_HV_H__ */
+
diff --git a/include/linux/muic/s2mu004-muic-notifier.h b/include/linux/muic/s2mu004-muic-notifier.h
new file mode 100644 (file)
index 0000000..450a60e
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * include/linux/muic/muic_notifier.h
+ *
+ * header file supporting MUIC 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 __MUIC_NOTIFIER_H__
+#define __MUIC_NOTIFIER_H__
+
+#include <linux/muic/muic.h>
+#if defined(CONFIG_CCIC_NOTIFIER)
+#include <linux/ccic/ccic_notifier.h>
+#endif
+
+/* MUIC notifier call chain command */
+typedef enum {
+       MUIC_NOTIFY_CMD_DETACH  = 0,
+       MUIC_NOTIFY_CMD_ATTACH,
+       MUIC_NOTIFY_CMD_LOGICALLY_DETACH,
+       MUIC_NOTIFY_CMD_LOGICALLY_ATTACH,
+       MUIC_PDIC_NOTIFY_CMD_ATTACH,
+       MUIC_PDIC_NOTIFY_CMD_DETACH,
+       PDIC_MUIC_NOTIFY_CMD_JIG_ATTACH,
+       PDIC_MUIC_NOTIFY_CMD_JIG_DETACH,
+} muic_notifier_cmd_t;
+
+/* MUIC notifier call sequence,
+ * largest priority number device will be called first. */
+typedef enum {
+       MUIC_NOTIFY_DEV_DOCK = 0,
+       MUIC_NOTIFY_DEV_MHL,
+       MUIC_NOTIFY_DEV_USB,
+       MUIC_NOTIFY_DEV_TSP,
+       MUIC_NOTIFY_DEV_CHARGER,
+       MUIC_NOTIFY_DEV_PDIC,
+       MUIC_NOTIFY_DEV_CPUIDLE,
+       MUIC_NOTIFY_DEV_CPUFREQ,
+#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER
+       MUIC_NOTIFY_DEV_MANAGER,
+#endif
+} muic_notifier_device_t;
+
+struct muic_notifier_struct {
+       muic_attached_dev_t attached_dev;
+       muic_notifier_cmd_t cmd;
+#if defined(CONFIG_CCIC_NOTIFIER)
+       CC_NOTI_ATTACH_TYPEDEF cxt;
+#endif
+       struct blocking_notifier_head notifier_call_chain;
+};
+
+#define MUIC_NOTIFIER_BLOCK(name)      \
+       struct notifier_block (name)
+
+/* muic notifier init/notify function
+ * this function is for JUST MUIC device driver.
+ * DON'T use function anywhrer else!!
+ */
+extern void muic_notifier_attach_attached_dev(muic_attached_dev_t new_dev);
+extern void muic_notifier_detach_attached_dev(muic_attached_dev_t cur_dev);
+extern void muic_pdic_notifier_attach_attached_dev(muic_attached_dev_t new_dev);
+extern void muic_pdic_notifier_detach_attached_dev(muic_attached_dev_t new_dev);
+extern void muic_notifier_logically_attach_attached_dev(muic_attached_dev_t new_dev);
+extern void muic_notifier_logically_detach_attached_dev(muic_attached_dev_t cur_dev);
+
+#if defined(CONFIG_CCIC_S2MU004)
+extern int muic_ccic_notifier_register(struct notifier_block *nb,
+               notifier_fn_t notifier, muic_notifier_device_t listener);
+extern int muic_ccic_notifier_unregister(struct notifier_block *nb);
+#endif
+/* muic notifier register/unregister API
+ * for used any where want to receive muic attached device attach/detach. */
+extern int muic_notifier_register(struct notifier_block *nb,
+               notifier_fn_t notifier, muic_notifier_device_t listener);
+extern int muic_notifier_unregister(struct notifier_block *nb);
+
+/* Choose a proper noti. interface for a test */
+extern void muic_notifier_set_new_noti(bool flag);
+
+#endif /* __MUIC_NOTIFIER_H__ */
diff --git a/include/linux/muic/s2mu004-muic-sysfs.h b/include/linux/muic/s2mu004-muic-sysfs.h
new file mode 100644 (file)
index 0000000..15712d6
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef __s2MU004_MUIC_SYSFS_H__
+#define __s2MU004_MUIC_SYSFS_H__
+
+extern void hv_muic_change_afc_voltage(int tx_data);
+int s2mu004_muic_init_sysfs(struct s2mu004_muic_data *muic_data);
+void s2mu004_muic_deinit_sysfs(struct s2mu004_muic_data *muic_data);
+
+#endif
diff --git a/include/linux/muic/s2mu004-muic.h b/include/linux/muic/s2mu004-muic.h
new file mode 100644 (file)
index 0000000..c27f7b2
--- /dev/null
@@ -0,0 +1,588 @@
+/*
+ * Copyright (C) 2015 Samsung Electronics
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#ifndef __S2MU004_MUIC_H__
+#define __S2MU004_MUIC_H__
+
+#include <linux/wakelock.h>
+#include <linux/muic/muic_core.h>
+#include <linux/muic/muic_interface.h>
+#include <linux/muic/s2mu004-muic-hv.h>
+
+#define MUIC_DEV_NAME  "muic-s2mu004"
+
+#define MASK_1BIT              (0x1)
+#define MASK_2BIT              (0x3)
+#define MASK_3BIT              (0x7)
+#define MASK_4BIT              (0xf)
+#define MASK_5BIT              (0x1f)
+#define MASK_6BIT              (0x3f)
+#define MASK_7BIT              (0x7f)
+#define MASK_8BIT              (0xff)
+
+#define MAX_BCD_RESCAN_CNT     5
+
+/* s2mu004 muic register read/write related information defines. */
+/* S2MU004 Control register */
+#define CTRL_SWITCH_OPEN_SHIFT 4
+#define CTRL_RAW_DATA_SHIFT            3
+#define CTRL_MANUAL_SW_SHIFT   2
+#define CTRL_WAIT_SHIFT                        1
+#define CTRL_INT_MASK_SHIFT            0
+
+#define CTRL_SWITCH_OPEN_MASK  (0x1 << CTRL_SWITCH_OPEN_SHIFT)
+#define CTRL_RAW_DATA_MASK             (0x1 << CTRL_RAW_DATA_SHIFT)
+#define CTRL_MANUAL_SW_MASK            (0x1 << CTRL_MANUAL_SW_SHIFT)
+#define CTRL_WAIT_MASK                 (0x1 << CTRL_WAIT_SHIFT)
+#define CTRL_INT_MASK_MASK             (0x1 << CTRL_INT_MASK_SHIFT)
+
+#if defined(CONFIG_MUIC_S2MU004_ENABLE_AUTOSW)
+#define CTRL_MASK                      (CTRL_SWITCH_OPEN_MASK | \
+                                               CTRL_MANUAL_SW_MASK | CTRL_WAIT_MASK | \
+                                               CTRL_INT_MASK_MASK)
+#else
+#define CTRL_MASK                      (CTRL_SWITCH_OPEN_MASK | \
+                                               CTRL_WAIT_MASK | CTRL_INT_MASK_MASK)
+#endif
+
+/* S2MU004 MUIC Interrupt 1 register */
+#define INT_RID_CHG_SHIFT              5
+#define INT_LKR_SHIFT                  4
+#define INT_LKP_SHIFT                  3
+#define INT_KP_SHIFT                   2
+#define INT_DETACH_SHIFT               1
+#define INT_ATTACH_SHIFT               0
+
+#define INT_RID_CHG_MASK               (0x1 << INT_RID_CHG_SHIFT)
+#define INT_LKR_MASK                   (0x1 << INT_LKR_SHIFT)
+#define INT_LKP_MASK                   (0x1 << INT_LKP_SHIFT)
+#define INT_KP_MASK                            (0x1 << INT_KP_SHIFT)
+#define INT_DETACH_MASK                        (0x1 << INT_DETACH_SHIFT)
+#define INT_ATTACH_MASK                        (0x1 << INT_ATTACH_SHIFT)
+
+/* S2MU004 MUIC Interrupt 2 register */
+#define INT_VBUS_OFF_SHIFT             7
+#define INT_AV_CHANGE_SHIFT            6
+#define INT_MHDL_SHIFT                 5
+#define INT_STUCKRCV_SHIFT             4
+#define INT_STUCK_SHIFT                        3
+#define INT_ADC_CHANGE_SHIFT   2
+#define INT_RSRV_ATTACH_SHIFT  1
+#define INT_CHG_DET_SHIFT              0
+
+#define INT_VBUS_OFF_MASK              (0x1 << INT_VBUS_OFF_SHIFT)
+#define INT_AV_CHANGE_MASK             (0x1 << INT_AV_CHANGE_SHIFT)
+#define INT_MHDL_MASK                  (0x1 << INT_MHDL_SHIFT)
+#define INT_STUCKRCV_MASK              (0x1 << INT_STUCKRCV_SHIFT)
+#define INT_STUCK_MASK                 (0x1 << INT_STUCK_SHIFT)
+#define INT_ADC_CHANGE_MASK            (0x1 << INT_ADC_CHANGE_SHIFT)
+#define INT_RSRV_ATTACH_MASK   (0x1 << INT_RSRV_ATTACH_SHIFT)
+#define INT_VBUS_ON_MASK               (0x1 << INT_CHG_DET_SHIFT)
+
+/* S2MU004 MUIC Interrupt 1 MASK register */
+#define INTm_RID_CHG_SHIFT             5
+#define INTm_LKR_SHIFT                 4
+#define INTm_LKP_SHIFT                 3
+#define INTm_KP_SHIFT                  2
+#define INTm_DETACH_SHIFT              1
+#define INTm_ATTACH_SHIFT              0
+
+#define INTm_RID_CHG_MASK              (0x1 << INTm_RID_CHG_SHIFT)
+#define INTm_LKR_MASK                  (0x1 << INTm_LKR_SHIFT)
+#define INTm_LKP_MASK                  (0x1 << INTm_LKP_SHIFT)
+#define INTm_KP_MASK                   (0x1 << INTm_KP_SHIFT)
+#define INTm_DETACH_MASK               (0x1 << INTm_DETACH_SHIFT)
+#define INTm_ATTACH_MASK               (0x1 << INTm_ATTACH_SHIFT)
+
+/* S2MU004 MUIC Interrupt 2 MASK register */
+#define INTm_VBUS_OFF_SHIFT            7
+#define INTm_AV_CHANGE_SHIFT   6
+#define INTm_MHDL_SHIFT                        5
+#define INTm_STUCKRCV_SHIFT            4
+#define INTm_STUCK_SHIFT               3
+#define INTm_ADC_CHANGE_SHIFT  2
+#define INTm_RSRV_ATTACH_SHIFT 1
+#define INTm_CHG_DET_SHIFT             0
+
+#define INTm_VBUS_OFF_MASK             (0x1 << INTm_VBUS_OFF_SHIFT)
+#define INTm_AV_CHANGE_MASK            (0x1 << INTm_AV_CHANGE_SHIFT)
+#define INTm_MHDL_MASK                 (0x1 << INTm_MHDL_SHIFT)
+#define INTm_STUCKRCV_MASK             (0x1 << INTm_STUCKRCV_SHIFT)
+#define INTm_STUCK_MASK                        (0x1 << INTm_STUCK_SHIFT)
+#define INTm_ADC_CHANGE_MASK   (0x1 << INTm_ADC_CHANGE_SHIFT)
+#define INTm_RSRV_ATTACH_MASK  (0x1 << INTm_RSRV_ATTACH_SHIFT)
+#define INTm_VBUS_ON_MASK              (0x1 << INTm_CHG_DET_SHIFT)
+
+/* S2MU004 MUIC Interrupt Maksing for pdic */
+#define INT_PDIC_MASK1                 (0xFC)
+#define INT_PDIC_MASK2                 (0x7A)
+
+/* S2MU004 ADC register */
+#define ADC_MASK                               (0x1f)
+#define ADC_CONVERSION_ERR_MASK                (0x1 << 7)
+
+/* S2MU004 Timing Set 1 & 2 register Timing table */
+#define KEY_PRESS_TIME_100MS           (0x00)
+#define KEY_PRESS_TIME_200MS           (0x10)
+#define KEY_PRESS_TIME_300MS           (0x20)
+#define KEY_PRESS_TIME_700MS           (0x60)
+
+#define LONGKEY_PRESS_TIME_300MS       (0x00)
+#define LONGKEY_PRESS_TIME_500MS       (0x02)
+#define LONGKEY_PRESS_TIME_1000MS      (0x07)
+#define LONGKEY_PRESS_TIME_1500MS      (0x0C)
+
+#define SWITCHING_WAIT_TIME_10MS       (0x00)
+#define SWITCHING_WAIT_TIME_210MS      (0xa0)
+
+/* S2MU004 MUIC Device Type 1 register */
+#define DEV_TYPE1_USB_OTG                      (0x1 << 7)
+#define DEV_TYPE1_DEDICATED_CHG                (0x1 << 6)
+#define DEV_TYPE1_CDP                          (0x1 << 5)
+#define DEV_TYPE1_T1_T2_CHG                    (0x1 << 4)
+#define DEV_TYPE1_UART                         (0x1 << 3)
+#define DEV_TYPE1_USB                          (0x1 << 2)
+#define DEV_TYPE1_AUDIO_2                      (0x1 << 1)
+#define DEV_TYPE1_AUDIO_1                      (0x1 << 0)
+#define DEV_TYPE1_USB_TYPES                    (DEV_TYPE1_USB_OTG | DEV_TYPE1_CDP | DEV_TYPE1_USB)
+#define DEV_TYPE1_CHG_TYPES                    (DEV_TYPE1_DEDICATED_CHG | DEV_TYPE1_CDP)
+
+/* S2MU004 MUIC Device Type 2 register */
+#define DEV_TYPE2_SDP_1P8S                     (0x1 << 7)
+#define DEV_TYPE2_AV                           (0x1 << 6)
+#define DEV_TYPE2_TTY                          (0x1 << 5)
+#define DEV_TYPE2_PPD                          (0x1 << 4)
+#define DEV_TYPE2_JIG_UART_OFF         (0x1 << 3)
+#define DEV_TYPE2_JIG_UART_ON          (0x1 << 2)
+#define DEV_TYPE2_JIG_USB_OFF          (0x1 << 1)
+#define DEV_TYPE2_JIG_USB_ON           (0x1 << 0)
+#define DEV_TYPE2_JIG_USB_TYPES                (DEV_TYPE2_JIG_USB_OFF | DEV_TYPE2_JIG_USB_ON)
+#define DEV_TYPE2_JIG_UART_TYPES       (DEV_TYPE2_JIG_UART_OFF)
+#define DEV_TYPE2_JIG_TYPES                    (DEV_TYPE2_JIG_UART_TYPES | DEV_TYPE2_JIG_USB_TYPES)
+
+/* S2MU004 MUIC Device Type 3 register */
+#define DEV_TYPE3_U200_CHG                     (0x1 << 7)
+#define DEV_TYPE3_AV_WITH_VBUS         (0x1 << 4)
+#define DEV_TYPE3_VBUS_R255                    (0x1 << 1)
+#define DEV_TYPE3_MHL                          (0x1 << 0)
+#define DEV_TYPE3_CHG_TYPE                     (DEV_TYPE3_U200_CHG | DEV_TYPE3_VBUS_R255)
+
+/* S2MU004 MUIC APPLE Device Type register */
+#define DEV_TYPE_APPLE_APPLE0P5A_CHG   (0x1 << 7)
+#define DEV_TYPE_APPLE_APPLE1A_CHG             (0x1 << 6)
+#define DEV_TYPE_APPLE_APPLE2A_CHG             (0x1 << 5)
+#define DEV_TYPE_APPLE_APPLE2P4A_CHG   (0x1 << 4)
+#define DEV_TYPE_APPLE_SDP_DCD_OUT             (0x1 << 3)
+#define DEV_TYPE_APPLE_RID_WAKEUP              (0x1 << 2)
+#define DEV_TYPE_APPLE_VBUS_WAKEUP             (0x1 << 1)
+#define DEV_TYPE_APPLE_BCV1P2_OR_OPEN  (0x1 << 0)
+
+/* S2MU004 MUIC CHG Type register */
+#define CHG_TYPE_VBUS_R255             (0x1 << 7)
+#define DEV_TYPE_U200                  (0x1 << 4)
+#define DEV_TYPE_SDP_1P8S              (0x1 << 3)
+#define DEV_TYPE_USB                   (0x1 << 2)
+#define DEV_TYPE_CDPCHG                        (0x1 << 1)
+#define DEV_TYPE_DCPCHG                        (0x1 << 0)
+#define DEV_TYPE_CHG_TYPE              (CHG_TYPE_VBUS_R255 | DEV_TYPE_U200 | DEV_TYPE_SDP_1P8S)
+
+/* S2MU004 MUIC AFC STATUS register (0x48) */
+#define MUIC_AFC_STATUS_VDNMON_SHIFT   4
+#define MUIC_AFC_STATUS_VDNMON_MASK    (MASK_1BIT << MUIC_AFC_STATUS_VDNMON_SHIFT)
+
+/* S2MU004 MUIC AFC CTRL1 register (0x49) */
+#define MUIC_AFC_CTRL1_AFC_EN_SHIFT                    7
+#define MUIC_AFC_CTRL1_CTRL_IDM_ON_SHIFT       6
+#define MUIC_AFC_CTRL1_VB_ADC_EN_SHIFT         5
+#define MUIC_AFC_CTRL1_DPVD_SEL_SHIFT          3
+#define MUIC_AFC_CTRL1_DNVD_SEL_SHIFT          1
+#define MUIC_AFC_CTRL1_DPDNVD_EN_SHIFT         0
+
+#define MUIC_AFC_CTRL1_AFC_EN_MASK                     (MASK_1BIT << MUIC_AFC_CTRL1_AFC_EN_SHIFT)
+#define MUIC_AFC_CTRL1_CTRL_IDM_ON_MASK                (MASK_1BIT << MUIC_AFC_CTRL1_CTRL_IDM_ON_SHIFT)
+#define MUIC_AFC_CTRL1_VB_ADC_EN_MASK          (MASK_1BIT << MUIC_AFC_CTRL1_VB_ADC_EN_SHIFT)
+#define MUIC_AFC_CTRL1_DPVD_SEL_MASK           (MASK_2BIT << MUIC_AFC_CTRL1_DPVD_SEL_SHIFT)
+#define MUIC_AFC_CTRL1_DNVD_SEL_MASK           (MASK_2BIT << MUIC_AFC_CTRL1_DNVD_SEL_SHIFT)
+#define MUIC_AFC_CTRL1_DPDNVD_EN_MASK          (MASK_1BIT << MUIC_AFC_CTRL1_DPDNVD_EN_SHIFT)
+
+/* S2MU004 MUIC AFC LOGIC CTRL2 register (0x5F) */
+#define MUIC_AFC_LOGIC_CTRL2_AFC_RST_SHIFT                                     7
+#define MUIC_AFC_LOGIC_CTRL2_AFC_REQUEST_OPT_REDUMP_SHIFT      6
+#define MUIC_AFC_LOGIC_CTRL2_UI_MASTER_SLAVE_SEL_SHIFT         5
+#define MUIC_AFC_LOGIC_CTRL2_PROTOCOL_SW_ON_SHFIT                      2
+#define MUIC_AFC_LOGIC_CTRL2_RX_COMPHY_SHIFT                           0
+
+#define MUIC_AFC_LOGIC_CTRL2_AFC_RST_MASK      (MASK_1BIT << MUIC_AFC_LOGIC_CTRL2_AFC_RST_SHIFT)
+#define MUIC_AFC_LOGIC_CTRL2_AFC_REQUEST_OPT_REDUMP_MASK (MASK_1BIT << MUIC_AFC_LOGIC_CTRL2_AFC_REQUEST_OPT_REDUMP_SHIFT)
+#define MUIC_AFC_LOGIC_CTRL2_UI_MASTER_SLAVE_SEL_MASK (MASK_1BIT << MUIC_AFC_LOGIC_CTRL2_UI_MASTER_SLAVE_SEL_SHIFT)
+#define MUIC_AFC_LOGIC_CTRL2_PROTOCOL_SW_ON_MASK (MASK_1BIT << MUIC_AFC_LOGIC_CTRL2_PROTOCOL_SW_ON_SHFIT)
+#define MUIC_AFC_LOGIC_CTRL2_RX_COMPHY_MASK    (MASK_2BIT << MUIC_AFC_LOGIC_CTRL2_RX_COMPHY_SHIFT)
+
+/* S2MU004 MUIC BCD RESCAN (0x6A) */
+#define MUIC_BCD_RESCAN_SHIFT          0
+
+#define MUIC_BCD_RESCAN_MASK           (MASK_1BIT << MUIC_BCD_RESCAN_SHIFT)
+
+/* S2MU004 MUIC TIMER SET3 register (0xCB) */
+#define TIMER_SET3_JIG_WAIT_TIME_SHIFT         5
+#define TIMER_SET3_ADC_PERIOD_SHIFT                    3
+#define TIMER_SET3_DCDTMRSET_SHIFT                     0
+
+#define TIMER_SET3_JIG_WAIT_TIME_MASK  (MASK_3BIT << TIMER_SET3_JIG_WAIT_TIME_SHIFT)
+#define TIMER_SET3_ADC_PERIOD_MASK     (MASK_2BIT << TIMER_SET3_ADC_PERIOD_SHIFT)
+#define TIMER_SET3_DCDTMRSET_MASK      (MASK_3BIT << TIMER_SET3_DCDTMRSET_SHIFT)
+
+/* S2MU004 MUIC CTRL2 register (0xCC) */
+#define MUIC_CTRL2_ADCEN_CNTR_SHIFT            4
+#define MUIC_CTRL2_DCDTMRSET_SHIFT             3
+#define MUIC_CTRL2_USB_2ND_EN_SHIFT            2
+#define MUIC_CTRL2_ADC_OFF_SHIFT               1
+#define MUIC_CTRL2_CPEN_SHIFT                  0
+
+#define MUIC_CTRL2_ADCEN_CNTR_MASK             (MASK_3BIT << MUIC_CTRL2_ADCEN_CNTR_SHIFT)
+#define MUIC_CTRL2_DCDTMRSET_MASK              (MASK_1BIT << MUIC_CTRL2_DCDTMRSET_SHIFT)
+#define MUIC_CTRL2_USB_2ND_EN_MASK             (MASK_1BIT << MUIC_CTRL2_USB_2ND_EN_SHIFT)
+#define MUIC_CTRL2_ADC_OFF_MASK                        (MASK_1BIT << MUIC_CTRL2_ADC_OFF_SHIFT)
+#define MUIC_CTRL2_CPEN_MASK                   (MASK_1BIT << MUIC_CTRL2_CPEN_SHIFT)
+
+/* S2MU004 CHARGER DET OTP register (0xCE) */
+#define CHARGER_DET_OTP_VDAT_REF_SHIFT 0
+
+#define CHARGER_DET_OTP_VDAT_REF_MASK (MASK_4BIT << CHARGER_DET_OTP_VDAT_REF_SHIFT)
+
+/* S2MU004 AFC_OTP6(RGB) register (0xDA) */
+#define MUIC_AFC_OTP6_JIG_QBAT_OFF_SHIFT               7
+#define MUIC_AFC_OTP6_FAST_LC_MODE_SHIFT               6
+#define MUIC_AFC_OTP6_NO_FAST_3L_LC_MODE_SHIFT 5
+#define MUIC_AFC_OTP6_EN_VBUS_DET_MUIC_SHIFT   4
+#define MUIC_AFC_OTP6_LED1_ITRIM_SHFIT                 3
+#define MUIC_AFC_OTP6_LED2_ITRIM_SHFIT                 2
+#define MUIC_AFC_OTP6_LED3_ITRIM_SHFIT                 1
+#define MUIC_AFC_OTP6_LED4_ITRIM_SHFIT                 0
+
+#define MUIC_AFC_OTP6_JIG_QBAT_OFF_MASK                        (MASK_1BIT << MUIC_AFC_OTP6_JIG_QBAT_OFF_SHIFT)
+#define MUIC_AFC_OTP6_FAST_LC_MODE_MASK                        (MASK_1BIT << MUIC_AFC_OTP6_FAST_LC_MODE_SHIFT)
+#define MUIC_AFC_OTP6_NO_FAST_3L_LC_MODE_MASK  (MASK_1BIT << MUIC_AFC_OTP6_NO_FAST_3L_LC_MODE_SHIFT)
+#define MUIC_AFC_OTP6_EN_VBUS_DET_MUIC_MASK            (MASK_1BIT << MUIC_AFC_OTP6_EN_VBUS_DET_MUIC_SHIFT)
+#define MUIC_AFC_OTP6_LED1_ITRIM_MASK                  (MASK_1BIT << MUIC_AFC_OTP6_LED1_ITRIM_SHFIT)
+#define MUIC_AFC_OTP6_LED2_ITRIM_MASK                  (MASK_1BIT << MUIC_AFC_OTP6_LED2_ITRIM_SHFIT)
+#define MUIC_AFC_OTP6_LED3_ITRIM_MASK                  (MASK_1BIT << MUIC_AFC_OTP6_LED3_ITRIM_SHFIT)
+#define MUIC_AFC_OTP6_LED4_ITRIM_MASK                  (MASK_1BIT << MUIC_AFC_OTP6_LED4_ITRIM_SHFIT)
+
+/*
+ * Manual Switch
+ * D- [7:5] / D+ [4:2] / CHARGER[1] / OTGEN[0]
+ * 000: Open all / 001: USB / 010: AUDIO / 011: UART / 100: V_AUDIO
+ * 00: Vbus to Open / 01: Vbus to Charger / 10: Vbus to MIC / 11: Vbus to VBout
+ */
+#define MANUAL_SW_JIG_EN               (0x1 << 0)
+
+#define MANUAL_SW_DM_SHIFT             5
+#define MANUAL_SW_DP_SHIFT             2
+#define MANUAL_SW_CHG_SHIFT            1
+#define MANUAL_SW_DM_DP_MASK   0xFC
+
+#define MANUAL_SW_OPEN                 (0x0)
+#define MANUAL_SW_USB                  (0x1 << MANUAL_SW_DM_SHIFT | 0x1 << MANUAL_SW_DP_SHIFT)
+#define MANUAL_SW_UART                 (0x2 << MANUAL_SW_DM_SHIFT | 0x2 << MANUAL_SW_DP_SHIFT)
+#define MANUAL_SW_UART2                        (0x3 << MANUAL_SW_DM_SHIFT | 0x3 << MANUAL_SW_DP_SHIFT)
+#define MANUAL_SW_AUDIO                        (0x0 << MANUAL_SW_DM_SHIFT | 0x0 << MANUAL_SW_DP_SHIFT) /* Not Used */
+
+#define MANUAL_SW_OTGEN                        (0x1)
+
+/* S2MU004_REG_MUIC_RID_CTRL */
+#define RID_CTRL_ADC_OFF_SHIFT 1
+#define RID_CTRL_ADC_OFF_MASK  (0x1 << RID_CTRL_ADC_OFF_SHIFT)
+
+#define WATER_DET_RETRY_CNT                            10
+#define WATER_CCIC_WAIT_DURATION_MS            4000
+#define WATER_DRY_RETRY_INTERVAL_MS            30000
+#define WATER_DRY_INTERVAL_MS                  10000
+#define WATER_DET_STABLE_DURATION_MS   2000
+#define DRY_DET_RETRY_CNT_MAX                  3
+#define RID_REFRESH_DURATION_MS                        100
+#define WATER_TOGGLE_WA_DURATION_US            20000
+
+/* s2mu004-muic macros */
+#define ENUM_STR(x, r) { case x: r = #x; break; }
+
+#define REQUEST_IRQ(_irq, _dev_id, _name, _func)                               \
+do {                                                                   \
+       ret = request_threaded_irq(_irq, NULL, _func,   \
+                               0, _name, _dev_id);     \
+       if (ret < 0) {                                                  \
+               pr_err("%s:%s Failed to request IRQ #%d: %d\n",         \
+                               MUIC_DEV_NAME, __func__, _irq, ret);    \
+               _irq = 0;                                               \
+       }                                                               \
+} while (0)
+
+#define FREE_IRQ(_irq, _dev_id, _name)                                 \
+do {                                                                   \
+       if (_irq) {                                                     \
+               free_irq(_irq, _dev_id);                                \
+               pr_info("%s:%s IRQ(%d):%s free done\n", MUIC_DEV_NAME,  \
+                               __func__, _irq, _name);                 \
+       }                                                               \
+} while (0)
+
+#define IS_WATER_ADC(adc)\
+               (((adc) > (ADC_GND)) && ((adc) < (ADC_OPEN)) \
+               ? 1 : 0)
+#define IS_AUDIO_ADC(adc)\
+               (((adc) >= (ADC_SEND_END)) && ((adc) <= (ADC_REMOTE_S12)) \
+               ? 1 : 0)
+#define IS_ACC_ADC(adc)\
+               (((adc) >= (ADC_RESERVED_VZW)) \
+               && ((adc) <= (ADC_AUDIOMODE_W_REMOTE)) \
+               ? 1 : 0)
+/* end of macros */
+
+/* S2MU004_REG_LDOADC_VSETH register */
+#define LDOADC_VSETH_MASK      0x1F
+#define LDOADC_VSETL_MASK      0x1F
+#define LDOADC_VSET_3V         0x1F
+#define LDOADC_VSET_2_7V       0x1C
+#define LDOADC_VSET_2_6V       0x0E
+#define LDOADC_VSET_2_4V       0x0C
+#define LDOADC_VSET_2_2V       0x0A
+#define LDOADC_VSET_2_0V       0x08
+#define LDOADC_VSET_1_8V       0x06
+#define LDOADC_VSET_1_7V       0x05
+#define LDOADC_VSET_1_6V       0x04
+#define LDOADC_VSET_1_5V       0x03
+#define LDOADC_VSET_1_4V       0x02
+#define LDOADC_VSET_1_2V       0x00
+#define LDOADC_VSETH_WAKE_HYS_SHIFT    6
+#define LDOADC_VSETH_WAKE_HYS_MASK     (0x1 << LDOADC_VSETH_WAKE_HYS_SHIFT)
+
+#define MANSW_OPEN                             (MANUAL_SW_OPEN)
+#define MANSW_USB                                      (MANUAL_SW_USB)
+#define MANSW_AUDIO                            (MANUAL_SW_AUDIO)
+#define MANSW_OTG                                      (MANUAL_SW_USB | MANUAL_SW_OTGEN)
+#define MANSW_UART_AP                          (MANUAL_SW_UART)
+#if IS_ENABLED(CONFIG_PMU_UART_SWITCH)
+#define MANSW_UART_CP                          (MANUAL_SW_UART)
+#else
+#define MANSW_UART_CP                          (MANUAL_SW_UART2)
+#endif
+#define MANSW_HICCUP                           (MANUAL_SW_UART2)
+#define MANSW_OPEN_RUSTPROOF   (MANUAL_SW_OPEN)
+
+enum s2mu004_muic_mode {
+       S2MU004_NONE_CABLE,
+       S2MU004_MUIC_ATTACH,
+       S2MU004_MUIC_DETACH,
+       S2MU004_MUIC_OTG,
+       S2MU004_MUIC_JIG,
+};
+
+enum s2mu004_muic_adc_mode {
+       S2MU004_ADC_ONESHOT,
+       S2MU004_ADC_PERIODIC,
+};
+
+typedef enum {
+       S2MU004_WATER_MUIC_IDLE,
+       S2MU004_WATER_MUIC_DET,
+       S2MU004_WATER_MUIC_CCIC_DET,
+       S2MU004_WATER_MUIC_CCIC_STABLE,
+       S2MU004_WATER_MUIC_CCIC_INVALID,
+} t_water_status;
+
+typedef enum {
+       S2MU004_WATER_DRY_MUIC_IDLE,
+       S2MU004_WATER_DRY_MUIC_DET,
+       S2MU004_WATER_DRY_MUIC_CCIC_DET,
+       S2MU004_WATER_DRY_MUIC_CCIC_INVALID,
+} t_water_dry_status;
+
+enum s2mu004_muic_enable {
+       S2MU004_DISABLE,
+       S2MU004_ENABLE,
+};
+
+typedef enum {
+       S2MU004_DETECT_NONE,
+       S2MU004_DETECT_DONE,
+} t_detect_status;
+
+typedef enum {
+       S2MU004_IRQ_CHECK_DONE,
+       S2MU004_IRQ_SKIP,
+} t_irq_status;
+
+enum s2mu004_muic_detect_dev_read_val {
+       DEVICE_TYPE1 = 0,
+       DEVICE_TYPE2,
+       DEVICE_TYPE3,
+       REV_ID,
+       ADC,
+       DEVICE_APPLE,
+       CHG_TYPE,
+       SC_STATUS2,
+       MAX_NUM,
+};
+
+/* muic chip specific internal data structure
+ * that setted at muic-xxxx.c file
+ */
+struct s2mu004_muic_data {
+       struct device *dev;
+       struct device *switch_device;
+       struct i2c_client *i2c; /* i2c addr: 0x7A; MUIC */
+       struct s2mu004_dev *s2mu004_dev;
+
+       struct mutex muic_mutex;
+       struct mutex afc_mutex;
+       struct mutex switch_mutex;
+
+       /* struct wakeup_source wakeup_src; */
+       struct wake_lock wake_lock;
+
+       /* model dependant mfd platform data */
+       struct s2mu004_platform_data *mfd_pdata;
+
+       /* model dependant muic platform data */
+       struct muic_platform_data *pdata;
+
+       void *if_data;
+
+       int irq_attach;
+       int irq_detach;
+       int irq_rid_chg;
+       int irq_vbus_on;
+       int irq_rsvd_attach;
+       int irq_adc_change;
+       int irq_av_charge;
+       int irq_vbus_off;
+
+       bool afc_check;
+       bool otg_state;
+       bool is_dcd_recheck;
+       bool is_otg_vboost;
+       bool is_otg_reboost;
+
+       muic_attached_dev_t new_dev;
+
+       int rev_id;
+       int adc;
+       int vbvolt;
+       int vmid;
+       int reg[MAX_NUM];
+       int re_detect;
+       int bcd_rescan_cnt;
+
+#if defined(CONFIG_MUIC_S2MU004_HV)
+       int retry_cnt;
+       int retry_qc_cnt;
+       int qc_prepare;
+       int afc_irq;
+       int irq_dnres;
+       int irq_mrxrdy;
+       int irq_mpnack;
+       int irq_vbadc;
+       int irq_vdnmon;
+#endif
+
+       /* muic common callback driver internal data */
+       struct sec_switch_data *switch_data;
+
+       /* muic support vps list */
+       bool muic_support_list[ATTACHED_DEV_NUM];
+
+       /* muic Device ID */
+       u8 muic_vendor;         /* Vendor ID */
+       u8 muic_version;        /* Version ID */
+       u8 ic_rev_id;           /* Rev ID */
+
+       /* W/A waiting for the charger ic */
+       struct delayed_work dcd_recheck;
+       struct delayed_work incomplete_check;
+#if defined(CONFIG_MUIC_S2MU004_HV)
+       struct delayed_work afc_check_vbadc;
+       struct delayed_work afc_cable_type_work;
+       struct delayed_work afc_irq_detect;
+       struct delayed_work afc_send_mpnack;
+       struct delayed_work afc_control_ping_retry;
+       struct delayed_work afc_qc_retry;
+       struct delayed_work afc_after_prepare;
+       struct delayed_work afc_check_interrupt;
+       struct delayed_work afc_mrxrdy;
+
+       muic_afc_data_t afc_data;
+
+       bool is_afc_muic_ready;
+       bool is_afc_handshaking;
+       bool is_afc_muic_prepare;
+       bool is_charger_ready;
+       bool is_mrxrdy;
+
+       int afc_count;
+
+       u8 tx_data;
+       u8 qc_hv;
+
+       /* muic status value */
+       u8 status1;
+       u8 status2;
+       u8 status3;
+       u8 status4;
+
+       /* muic hvcontrol value */
+       u8 hvcontrol1;
+       u8 hvcontrol2;
+#endif
+};
+
+extern struct muic_platform_data muic_pdata;
+
+int s2mu004_muic_com_to_open_with_vbus(struct s2mu004_muic_data *muic_data);
+int s2mu004_muic_com_to_open(struct s2mu004_muic_data *muic_data);
+int s2mu004_muic_com_to_usb(struct s2mu004_muic_data *muic_data);
+int s2mu004_muic_com_to_otg(struct s2mu004_muic_data *muic_data);
+int s2mu004_muic_com_to_uart(struct s2mu004_muic_data *muic_data);
+int s2mu004_muic_com_to_audio(struct s2mu004_muic_data *muic_data);
+int s2mu004_muic_recheck_adc(struct s2mu004_muic_data *muic_data);
+int s2mu004_muic_get_vbus_state(struct s2mu004_muic_data *muic_data);
+void s2mu004_muic_control_vbus_det(struct s2mu004_muic_data *muic_data,
+                                  bool enable);
+int s2mu004_i2c_read_byte(struct i2c_client *client, u8 command);
+int s2mu004_i2c_write_byte(struct i2c_client *client, u8 command, u8 value);
+extern void s2mu004_muic_dcd_rescan(struct s2mu004_muic_data *muic_data);
+extern int s2mu004_muic_control_rid_adc(struct s2mu004_muic_data *muic_data,
+                                       bool enable);
+extern int s2mu004_muic_bcd_rescan(struct s2mu004_muic_data *muic_data);
+int s2mu004_muic_jig_on(struct s2mu004_muic_data *muic_data);
+void s2mu004_muic_control_vbus_det(struct s2mu004_muic_data *muic_data,
+                                  bool enable);
+
+int s2mu004_set_gpio_uart_sel(struct s2mu004_muic_data *muic_data,
+                             int uart_sel);
+int s2mu004_init_interface(struct s2mu004_muic_data *muic_data,
+                          struct muic_interface_t *muic_if);
+#if IS_ENABLED(CONFIG_CCIC_S2MU004)
+int s2mu004_muic_refresh_adc(struct s2mu004_muic_data *muic_data);
+#endif
+#if IS_ENABLED(CONFIG_SEC_FACTORY)
+int s2mu004_muic_set_otg_reg(struct s2mu004_muic_data *muic_data, bool on);
+#else
+int s2mu004_muic_get_otg_state(void);
+#endif
+#endif /* __S2MU004_MUIC_H__ */
diff --git a/include/linux/sec_sysfs.h b/include/linux/sec_sysfs.h
new file mode 100644 (file)
index 0000000..51fdee2
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef SEC_SYSFS_H
+#define SEC_SYSFS_H
+
+#ifdef CONFIG_SEC_SYSFS
+extern struct device *sec_device_create(void *drvdata, const char *fmt);
+extern void sec_device_destroy(dev_t devt);
+#else
+static inline struct device *sec_device_create(void *drvdata, const char *fmt)
+{
+       pr_err("No rule to make sec sysfs\n");
+       return NULL;
+}
+static inline void sec_device_destroy(dev_t devt)
+{
+       return;
+}
+#endif
+
+#endif /* SEC_SYSFS_H */
index 3ccfce15ee8c40aa50087134be13fae6c7418d8e..6a8ad3a80e14b843a2bef5f752026d9daa945ef7 100644 (file)
@@ -39,6 +39,9 @@ static int pause_on_oops_flag;
 static DEFINE_SPINLOCK(pause_on_oops_lock);
 bool crash_kexec_post_notifiers;
 int panic_on_warn __read_mostly;
+#ifdef CONFIG_MUIC_S2MU004
+extern void s2mu004_muic_set_auto(void);
+#endif
 
 int panic_timeout = CONFIG_PANIC_TIMEOUT;
 EXPORT_SYMBOL_GPL(panic_timeout);
@@ -139,6 +142,10 @@ void panic(const char *fmt, ...)
        int old_cpu, this_cpu;
        bool _crash_kexec_post_notifiers = crash_kexec_post_notifiers;
 
+#ifdef CONFIG_MUIC_S2MU004
+       s2mu004_muic_set_auto();
+#endif
+
        /*
        * dbg_snapshot_early_panic is for supporting wapper functions
        * to users need to run SoC specific function in NOT interrupt