};
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";
source "drivers/misc/Kconfig"
+source "drivers/muic/Kconfig"
+
+source "drivers/ifconn/Kconfig"
source "drivers/ide/Kconfig"
source "drivers/scsi/Kconfig"
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/
--- /dev/null
+#
+# 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.
--- /dev/null
+#
+# Makefile for usb typec manager drivers
+#
+
+obj-$(CONFIG_IFCONN_MANAGER) += ifconn_manager.o
+obj-$(CONFIG_IFCONN_NOTIFIER) += ifconn_notifier.o
--- /dev/null
+#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");
--- /dev/null
+#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);
--- /dev/null
+#
+# 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.
--- /dev/null
+#
+# 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
--- /dev/null
+/* 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 */
+};
--- /dev/null
+/* 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, ¬i);
+ if (ret)
+ pr_err("%s prev_dev failed\n", __func__);
+
+ ret = muic_core_handle_attached_new_dev(muic_pdata, new_dev, ¬i);
+ 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);
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+#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);
+
--- /dev/null
+#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);
--- /dev/null
+/*
+ * 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
--- /dev/null
+
+#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);
+
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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");
#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
--- /dev/null
+/*
+ * include/linux/muic/ccic_notifier.h
+ *
+ * header file supporting CCIC notifier call chain information
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ * Seung-Jin Hahn <sjin.hahn@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef __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__ */
+
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+#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 */
--- /dev/null
+/*
+ * 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__ */
+
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+#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
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+#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 */
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);
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