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