| /* |
| * muic_vps.c |
| * |
| * Copyright (C) 2014 Samsung Electronics |
| * Thomas Ryu <smilesr.ryu@samsung.com> |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| * |
| */ |
| |
| #include <linux/gpio.h> |
| #include <linux/i2c.h> |
| #include <linux/interrupt.h> |
| #include <linux/slab.h> |
| #include <linux/platform_device.h> |
| #include <linux/module.h> |
| #include <linux/delay.h> |
| #include <linux/host_notify.h> |
| |
| #include <linux/muic/muic.h> |
| |
| #if defined(CONFIG_MUIC_NOTIFIER) |
| #include <linux/muic/muic_notifier.h> |
| #endif /* CONFIG_MUIC_NOTIFIER */ |
| |
| #if defined (CONFIG_OF) |
| #include <linux/of_device.h> |
| #include <linux/of_gpio.h> |
| #endif /* CONFIG_OF */ |
| |
| #include "muic-internal.h" |
| #include "muic_apis.h" |
| #include "muic_i2c.h" |
| #include "muic_vps.h" |
| |
| /* 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) |
| |
| /* Device Type 2 register */ |
| #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) |
| |
| /* Device Type 3 register */ |
| #define DEV_TYPE3_U200_CHG (0x1 << 6) |
| #define DEV_TYPE3_AV_WITH_VBUS (0x1 << 4) |
| #define DEV_TYPE3_NO_STD_CHG (0x1 << 2) |
| #define DEV_TYPE3_MHL (0x1 << 0) |
| #define DEV_TYPE3_CHG_TYPE (DEV_TYPE3_U200_CHG | DEV_TYPE3_NO_STD_CHG) |
| |
| static struct vps_cfg cfg_MHL = { |
| .name = "MHL", |
| .attr = MATTR(VCOM_OPEN, VB_ANY) |
| }; |
| static struct vps_cfg cfg_OTG = { |
| .name = "OTG", |
| .attr = MATTR(VCOM_USB, VB_ANY), |
| }; |
| static struct vps_cfg cfg_VZW_ACC = { |
| .name = "VZW Accessory", |
| .attr = MATTR(VCOM_OPEN, VB_ANY), |
| }; |
| static struct vps_cfg cfg_VZW_INCOMPATIBLE = { |
| .name = "VZW Incompatible", |
| .attr = MATTR(VCOM_OPEN, VB_ANY), |
| }; |
| static struct vps_cfg cfg_RDU_TA = { |
| .name = "RDU TA", |
| .attr = MATTR(VCOM_OPEN, VB_HIGH) | MATTR_CDET_SUPP, |
| }; |
| static struct vps_cfg cfg_HMT = { |
| .name = "HMT", |
| .attr = MATTR(VCOM_USB, VB_ANY), |
| }; |
| static struct vps_cfg cfg_AUDIODOCK = { |
| .name = "Audiodock", |
| .attr = MATTR(VCOM_USB, VB_HIGH), |
| }; |
| static struct vps_cfg cfg_USB_LANHUB = { |
| .name = "USB LANHUB", |
| .attr = MATTR(VCOM_OPEN, VB_ANY), |
| }; |
| static struct vps_cfg cfg_CHARGING_CABLE = { |
| .name = "Charging Cable", |
| .attr = MATTR(VCOM_OPEN, VB_ANY), |
| }; |
| static struct vps_cfg cfg_GAMEPAD = { |
| .name = "Game Pad", |
| .attr = MATTR(VCOM_USB, VB_ANY), |
| }; |
| static struct vps_cfg cfg_TYPE1_CHG = { |
| .name = "TYPE1 Charger", |
| .attr = MATTR(VCOM_OPEN, VB_HIGH) | MATTR_CDET_SUPP, |
| }; |
| static struct vps_cfg cfg_JIG_USB_OFF = { |
| .name = "Jig USB Off", |
| .attr = MATTR(VCOM_USB, VB_HIGH) | MATTR_FACT_SUPP, |
| }; |
| static struct vps_cfg cfg_JIG_USB_ON = { |
| .name = "Jig USB On", |
| .attr = MATTR(VCOM_USB, VB_HIGH) | MATTR_FACT_SUPP, |
| }; |
| static struct vps_cfg cfg_DESKDOCK = { |
| .name = "Deskdock", |
| .attr = MATTR(VCOM_OPEN, VB_ANY), |
| }; |
| static struct vps_cfg cfg_TYPE2_CHG = { |
| .name = "TYPE2 Charger", |
| .attr = MATTR(VCOM_OPEN, VB_HIGH), |
| }; |
| static struct vps_cfg cfg_JIG_UART_OFF = { |
| .name = "Jig UART Off", |
| .attr = MATTR(VCOM_UART, VB_ANY) | MATTR_FACT_SUPP, |
| }; |
| //CONFIG_SEC_FACTORY |
| static struct vps_cfg cfg_JIG_UART_ON = { |
| .name = "Jig UART On", |
| .attr = MATTR(VCOM_UART, VB_ANY) | MATTR_FACT_SUPP, |
| }; |
| static struct vps_cfg cfg_TA = { |
| .name = "TA", |
| .attr = MATTR(VCOM_OPEN, VB_HIGH) | MATTR_CDET_SUPP, |
| }; |
| static struct vps_cfg cfg_USB = { |
| .name = "USB", |
| .attr = MATTR(VCOM_OPEN, VB_HIGH) | MATTR_CDET_SUPP, |
| }; |
| static struct vps_cfg cfg_CDP = { |
| .name = "CDP", |
| .attr = MATTR(VCOM_OPEN, VB_HIGH) | MATTR_CDET_SUPP, |
| }; |
| static struct vps_cfg cfg_UNDEFINED_CHARGING = { |
| .name = "Undefined Charging", |
| .attr = MATTR(VCOM_OPEN, VB_HIGH) | MATTR_SUPP, |
| }; |
| |
| static struct vps_tbl_data vps_table[] = { |
| [MDEV(OTG)] = {0x00, "GND", &cfg_OTG,}, |
| [MDEV(MHL)] = {0xfe, "1K", &cfg_MHL,}, |
| /* 0x01 ~ 0x0D : Remote Sx Button */ |
| [MDEV(VZW_ACC)] = {0x0e, "28.7K", &cfg_VZW_ACC,}, |
| [MDEV(VZW_INCOMPATIBLE)] = {0x0f, "34K", &cfg_VZW_INCOMPATIBLE,}, |
| [MDEV(RDU_TA)] = {0x10, "40.2K", &cfg_RDU_TA,}, |
| [MDEV(HMT)] = {0x11, "49.9K", &cfg_HMT,}, |
| [MDEV(AUDIODOCK)] = {0x12, "64.9K", &cfg_AUDIODOCK,}, |
| [MDEV(USB_LANHUB)] = {0x13, "80.07K", &cfg_USB_LANHUB,}, |
| [MDEV(CHARGING_CABLE)] = {0x14, "102K", &cfg_CHARGING_CABLE,}, |
| [MDEV(GAMEPAD)] = {0x15, "121K", &cfg_GAMEPAD,}, |
| /* 0x16: UART Cable */ |
| [MDEV(TYPE1_CHG)] = {0x17, "200K", &cfg_TYPE1_CHG,}, |
| [MDEV(JIG_USB_OFF)] = {0x18, "255K", &cfg_JIG_USB_OFF,}, |
| [MDEV(JIG_USB_ON)] = {0x19, "301K", &cfg_JIG_USB_ON,}, |
| [MDEV(DESKDOCK)] = {0x1a, "365K", &cfg_DESKDOCK,}, |
| [MDEV(TYPE2_CHG)] = {0x1b, "442K", &cfg_TYPE2_CHG,}, |
| [MDEV(JIG_UART_OFF)] = {0x1c, "523K", &cfg_JIG_UART_OFF,}, |
| [MDEV(JIG_UART_ON)] = {0x1d, "619K", &cfg_JIG_UART_ON,}, |
| /* 0x1e: Audio Mode with Remote */ |
| [MDEV(TA)] = {0x1f, "OPEN", &cfg_TA,}, |
| [MDEV(USB)] = {0x1f, "OPEN", &cfg_USB,}, |
| [MDEV(CDP)] = {0x1f, "OPEN", &cfg_CDP,}, |
| [MDEV(UNDEFINED_CHARGING)] = {0xfe, "UNDEFINED", &cfg_UNDEFINED_CHARGING,}, |
| [ATTACHED_DEV_NUM] = {0x00, "NUM", NULL,}, |
| }; |
| |
| static bool mdev_undefined_range(int adc) |
| { |
| switch (adc) { |
| case ADC_SEND_END ... ADC_REMOTE_S12: |
| case ADC_UART_CABLE: |
| case ADC_AUDIOMODE_W_REMOTE: |
| return true; |
| default: |
| break; |
| } |
| |
| return false; |
| } |
| |
| static bool vps_is_acceptable(muic_data_t *pmuic, int adc) |
| { |
| if (pmuic->attached_dev == ATTACHED_DEV_HMT_MUIC) { |
| if ((adc == ADC_OPEN) || (adc == ADC_HMT)) |
| return true; |
| else |
| return false; |
| } else if (pmuic->attached_dev == ATTACHED_DEV_GAMEPAD_MUIC) { |
| if ((adc == ADC_OPEN) || (adc == ADC_GAMEPAD) || (adc == ADC_GND)) |
| return true; |
| else |
| return false; |
| } else |
| return true; |
| } |
| |
| struct vps_tbl_data * mdev_to_vps(muic_attached_dev_t mdev) |
| { |
| if (mdev >= ATTACHED_DEV_NUM) { |
| pr_err("%s Out of range mdev=%d\n", __func__, mdev); |
| return NULL; |
| } |
| |
| return &vps_table[mdev]; |
| } |
| |
| bool vps_name_to_mdev(const char *name, int *sdev) |
| { |
| struct vps_tbl_data *pvps; |
| int mdev; |
| |
| for (mdev = MDEV(NONE); mdev < ATTACHED_DEV_NUM; mdev++) { |
| pvps = &vps_table[mdev]; |
| if (!pvps->cfg) |
| continue; |
| |
| if (!strcmp(pvps->cfg->name, name)){ |
| break; |
| } |
| } |
| |
| if (mdev >= ATTACHED_DEV_NUM) { |
| pr_err("%s Out of range mdev=%d, %s\n", __func__, |
| mdev, name); |
| return false; |
| } |
| |
| pr_debug("%s:%s->[%2d]\n", __func__, pvps->cfg->name, mdev); |
| |
| *sdev = mdev; |
| |
| return true; |
| } |
| |
| bool vps_is_supported_dev(muic_attached_dev_t mdev) |
| { |
| struct vps_tbl_data *pvps = mdev_to_vps(mdev); |
| int attr; |
| |
| if (!pvps || !pvps->cfg) |
| return false; |
| |
| attr = pvps->cfg->attr; |
| |
| if (MATTR_TO_SUPP(attr)) |
| return true; |
| |
| return false; |
| } |
| |
| void vps_update_supported_attr(muic_attached_dev_t mdev, bool supported) |
| { |
| struct vps_tbl_data *pvps = mdev_to_vps(mdev); |
| |
| if (!pvps || !pvps->cfg) |
| return; |
| |
| if (supported) |
| pvps->cfg->attr |= MATTR_SUPP; |
| else |
| pvps->cfg->attr &= (~MATTR_SUPP) & 0xFFFFFFFF; |
| } |
| |
| bool vps_is_factory_dev(muic_attached_dev_t mdev) |
| { |
| struct vps_tbl_data *pvps = mdev_to_vps(mdev); |
| int attr; |
| |
| if (!pvps || !pvps->cfg) |
| return false; |
| |
| attr = pvps->cfg->attr; |
| |
| if (MATTR_TO_FACT(attr)) |
| return true; |
| |
| return false; |
| } |
| |
| static bool vps_is_1k_mhl_cable(vps_data_t *pmsr) |
| { |
| return pmsr->t.adc1k ? true : false; |
| } |
| |
| static bool vps_is_adc(vps_data_t *pmsr, struct vps_tbl_data *pvps) |
| { |
| if (pmsr->t.adc == pvps->adc) |
| return true; |
| |
| return false; |
| } |
| |
| |
| static bool vps_is_vbvolt(vps_data_t *pmsr, struct vps_tbl_data *pvps) |
| { |
| int attr = pvps->cfg->attr; |
| |
| if (pmsr->t.vbvolt == MATTR_TO_VBUS(attr)) |
| return true; |
| |
| if (MATTR_TO_VBUS(attr) == VB_ANY) |
| return true; |
| |
| return false; |
| } |
| |
| /* Check it the resolved device type is treated as a different one |
| * when VBUS also comes along. |
| */ |
| int resolve_twin_mdev(int mdev, bool vbus) |
| { |
| if (vbus) { |
| if (mdev == MDEV(DESKDOCK)) { |
| pr_info("%s: mdev:%d vbus:%d\n",__func__, mdev, vbus); |
| return MDEV(DESKDOCK_VB); |
| } |
| else if (mdev == MDEV(JIG_UART_ON)) { |
| pr_info("%s: mdev:%d vbus:%d\n",__func__, mdev, vbus); |
| return MDEV(JIG_UART_ON_VB); |
| } |
| } |
| |
| return 0; |
| } |
| |
| /* |
| * To filter out the chargers which don't support AFC, |
| * such as LO. |
| */ |
| bool vps_is_hv_ta(vps_data_t *pvps) |
| { |
| if (pvps->t.chgtyp == CHGTYP_DEDICATED_CHARGER) |
| return true; |
| |
| return false; |
| } |
| |
| int vps_chgtyp_to_dev(int prchgtyp, int chgtyp) |
| { |
| int ret = -1; |
| |
| switch (prchgtyp) { |
| case PRCHGTYP_APPLE_500MA: |
| case PRCHGTYP_APPLE_1A: |
| case PRCHGTYP_APPLE_2A: |
| case PRCHGTYP_APPLE_12W: |
| ret = ATTACHED_DEV_TA_MUIC; |
| goto out; |
| default: |
| break; |
| } |
| |
| switch (chgtyp) { |
| case CHGTYP_USB: |
| ret = ATTACHED_DEV_USB_MUIC; |
| break; |
| case CHGTYP_CDP: |
| ret = ATTACHED_DEV_CDP_MUIC; |
| break; |
| case CHGTYP_DEDICATED_CHARGER: |
| ret = ATTACHED_DEV_TA_MUIC; |
| break; |
| case CHGTYP_500MA: |
| case CHGTYP_1A: |
| case CHGTYP_SPECIAL_3_3V_CHARGER: |
| ret = ATTACHED_DEV_TA_MUIC; |
| break; |
| case CHGTYP_NO_VOLTAGE: |
| default: |
| pr_err("%s: Undefined chgtyp %d\n",__func__, chgtyp); |
| } |
| |
| out: |
| return ret; |
| } |
| |
| int resolve_dev_based_on_adc_chgtype(muic_data_t *pmuic, vps_data_t *pmsr) |
| { |
| int dev_type; |
| |
| pr_info("%s: adc=%02x, chgtyp=%02x\n",__func__, pmsr->t.adc, pmsr->t.chgtyp); |
| |
| switch(pmsr->t.adc){ |
| case ADC_OPEN: |
| dev_type = vps_chgtyp_to_dev(pmuic->vps.t.prchgtyp, pmsr->t.chgtyp); |
| if (dev_type < 0) { |
| pr_info("%s not able to resolve using ADC and CHGTYPE[OPEN]\n",__func__); |
| return -1; |
| } |
| break; |
| case ADC_CEA936ATYPE1_CHG: |
| case ADC_219: |
| if(pmsr->t.chgtyp == CHGTYP_DEDICATED_CHARGER) |
| dev_type = ATTACHED_DEV_TA_MUIC; |
| else if(pmsr->t.chgtyp == CHGTYP_UNOFFICIAL_CHARGER) |
| dev_type = ATTACHED_DEV_TA_MUIC; |
| else if(pmsr->t.chgtyp == CHGTYP_CDP) |
| dev_type = ATTACHED_DEV_CDP_MUIC; |
| else if(pmsr->t.chgtyp == CHGTYP_USB) |
| dev_type = ATTACHED_DEV_USB_MUIC; |
| else { |
| pr_info("%s not able to resolve using ADC and CHGTYPE[219K]\n",__func__); |
| return -1; |
| } |
| break; |
| case ADC_RDU_TA: |
| if (pmsr->t.chgtyp == CHGTYP_DEDICATED_CHARGER) |
| dev_type = ATTACHED_DEV_RDU_TA_MUIC; |
| else { |
| pr_info("%s abnormal RDU_TA\n",__func__); |
| return -1; |
| } |
| break; |
| default: |
| pr_info("%s not able to resolve using ADC and CHGTYPE\n",__func__); |
| return -1; |
| } |
| |
| return dev_type; |
| } |
| |
| int vps_find_attached_dev(muic_data_t *pmuic, muic_attached_dev_t *pdev, int *pintr) |
| { |
| struct vps_tbl_data *pvps; |
| muic_attached_dev_t new_dev = ATTACHED_DEV_UNKNOWN_MUIC, mdev; |
| vps_data_t *pmsr = &pmuic->vps; |
| int attr, ret = 0; |
| int intr = MUIC_INTR_ATTACH; |
| int chgdet_dev = 0; |
| |
| if (pmuic->discard_interrupt) { |
| pr_info("%s:%s Under ADC mode change.\n", MUIC_DEV_NAME, __func__); |
| return -1; |
| } |
| |
| if (pmuic->afc_water_disable) { |
| if (pmsr->t.vbvolt == VB_HIGH) { |
| pr_info("%s water\n", __func__); |
| pmuic->is_hiccup_mode = true; |
| *pintr = intr = MUIC_INTR_ATTACH; |
| *pdev = new_dev = ATTACHED_DEV_UNDEFINED_RANGE_MUIC; |
| return 0; |
| } |
| } |
| |
| if ((pmsr->t.vbvolt == VB_HIGH) && pmsr->t.chgdetrun && |
| (pmsr->t.adc != ADC_DESKDOCK)) { |
| pr_info("%s:%s chgdet is running.\n", MUIC_DEV_NAME, __func__); |
| return -1; |
| } |
| |
| for (mdev = MDEV(NONE); mdev < ATTACHED_DEV_NUM; mdev++) { |
| pvps = &vps_table[mdev]; |
| if (!pvps->cfg) |
| continue; |
| |
| attr = pvps->cfg->attr; |
| |
| if (vps_is_1k_mhl_cable(pmsr)) { |
| new_dev = mdev = MDEV(MHL); |
| pr_info("%s:%s MHL found at mdev:%d(%s)\n", |
| MUIC_DEV_NAME, __func__, mdev, pvps->cfg->name); |
| break; |
| } |
| |
| if (!vps_is_adc(pmsr, pvps)) |
| continue; |
| |
| if (!vps_is_vbvolt(pmsr, pvps)) |
| continue; |
| |
| if(MATTR_TO_CDET(attr)){ |
| if (!pmsr->t.chgtyp) { |
| intr = MUIC_INTR_DETACH; |
| pr_info("%s:%s No chgtyp. Assumes detach.\n", MUIC_DEV_NAME, __func__); |
| break; |
| } |
| |
| if (pmsr->t.DCDTimedout) { |
| pr_info("%s:%s DCDTimedout. Forced To USB\n", MUIC_DEV_NAME, __func__); |
| new_dev = mdev = MDEV(USB); |
| break; |
| } |
| |
| /* some function for cdeten check */ |
| chgdet_dev = resolve_dev_based_on_adc_chgtype(pmuic,pmsr); |
| if (chgdet_dev < 0) { |
| chgdet_dev = 0; |
| continue; |
| } |
| } |
| |
| pr_info("%s:%s vps table match found at [1]mdev:%d(%s)\n", |
| MUIC_DEV_NAME, __func__, mdev, pvps->cfg->name); |
| |
| new_dev = chgdet_dev ? chgdet_dev : mdev; |
| |
| break; |
| } |
| |
| /* do nothing */ |
| if (ret < 0) |
| return -1; |
| |
| /* Check Undefined range */ |
| if (pmuic->undefined_range) { |
| if ((pmsr->t.vbvolt == VB_HIGH) && |
| mdev_undefined_range(pmsr->t.adc)) { |
| pr_info("%s:%s undefined range adc:%02x\n", |
| MUIC_DEV_NAME, __func__, pmsr->t.adc); |
| |
| *pintr = intr = MUIC_INTR_ATTACH; |
| *pdev = new_dev = ATTACHED_DEV_UNDEFINED_RANGE_MUIC; |
| return 0; |
| } |
| } |
| |
| if (!vps_is_acceptable(pmuic, pmsr->t.adc)) { |
| pr_info("%s:%s Unacceptable adc(0x%02x) for HMT -> discarded.\n", |
| MUIC_DEV_NAME, __func__, pmsr->t.adc); |
| |
| com_to_open_with_vbus(pmuic); |
| return -1; |
| } |
| |
| if (mdev == ATTACHED_DEV_NUM) { |
| /* Check the cable types which are not listed in the vps table if vbus is. */ |
| if (pmsr->t.vbvolt == VB_HIGH) { |
| new_dev = ATTACHED_DEV_UNDEFINED_CHARGING_MUIC; |
| pr_info("%s:%s unsupported ID + VB [%2d]\n", |
| MUIC_DEV_NAME, __func__, new_dev); |
| } |
| else |
| { |
| intr = MUIC_INTR_DETACH; |
| new_dev = ATTACHED_DEV_NONE_MUIC; |
| if(pvps->cfg) |
| pr_info("%s:%s vps table match found at [2]mdev:%d(%s)\n", |
| MUIC_DEV_NAME, __func__, mdev, pvps->cfg->name); |
| } |
| } else { |
| /* Check if the cable type is supported with the attr of the cable. |
| */ |
| |
| int twin_mdev = 0; |
| |
| if (vps_is_supported_dev(new_dev)) { |
| if ((twin_mdev = resolve_twin_mdev(new_dev, pmsr->t.vbvolt))) { |
| new_dev = twin_mdev; |
| pr_info("%s:Supported twin mdev-> %d\n", __func__, twin_mdev); |
| } else |
| pr_info("%s:Supported.\n", __func__); |
| |
| } else if(pmsr->t.vbvolt && (intr == MUIC_INTR_ATTACH) && |
| !MATTR_TO_NOCHG(attr)) { |
| new_dev = ATTACHED_DEV_UNDEFINED_CHARGING_MUIC; |
| pr_info("%s:Unsupported->UNDEFINED_CHARGING\n", __func__); |
| } else { |
| intr = MUIC_INTR_DETACH; |
| new_dev = ATTACHED_DEV_UNKNOWN_MUIC; |
| pr_info("%s:Unsupported->Discarded.\n", __func__); |
| } |
| } |
| |
| *pintr = intr; |
| *pdev = new_dev; |
| |
| return ret; |
| } |
| |
| #if !defined(CONFIG_SAMSUNG_PRODUCT_SHIP) |
| void vps_show_table(void) |
| { |
| struct vps_tbl_data *pvps; |
| int mdev, attr; |
| |
| pr_info(" %4s%6s%10s %12s %29s\n", "dev", "ADC", "(RID)", "attr", "vps_name"); |
| for (mdev = MDEV(NONE); mdev < ATTACHED_DEV_NUM; mdev++) { |
| |
| pvps = &vps_table[mdev]; |
| |
| if (!pvps->cfg) |
| continue; |
| |
| attr = pvps->cfg->attr; |
| |
| pr_info(" [%2d] = %02x(%10s) %08x:%c:%c %28s\n", mdev, |
| pvps->adc, pvps->rid, pvps->cfg->attr, |
| MATTR_TO_SUPP(attr) ? 'S' : '_', |
| MATTR_TO_FACT(attr) ? 'F' : '_', |
| pvps->cfg->name); |
| } |
| |
| pr_info("done.\n"); |
| |
| } |
| #endif |
| |
| void vps_show_supported_list(void) |
| { |
| struct vps_tbl_data *pvps; |
| int mdev, attr; |
| |
| pr_info(" %4s%6s%10s %12s %30s\n", "dev", "ADC", "(RID)", "attr", "vps_name"); |
| for (mdev = MDEV(NONE); mdev < ATTACHED_DEV_NUM; mdev++) { |
| |
| pvps = &vps_table[mdev]; |
| |
| if (!pvps->cfg) |
| continue; |
| |
| attr = pvps->cfg->attr; |
| |
| if (!MATTR_TO_SUPP(attr)) |
| continue; |
| |
| pr_info(" [%2d] = %02x(%10s) %08x:%c:%c %28s\n", mdev, |
| pvps->adc, pvps->rid, pvps->cfg->attr, |
| MATTR_TO_SUPP(attr) ? 'S' : '_', |
| MATTR_TO_FACT(attr) ? 'F' : '_', |
| pvps->cfg->name); |
| |
| |
| } |
| |
| pr_info("done.\n"); |
| |
| } |
| |
| |
| static int resolve_dedicated_dev(muic_data_t *pmuic, muic_attached_dev_t *pdev, int *pintr) |
| { |
| muic_attached_dev_t new_dev = ATTACHED_DEV_UNKNOWN_MUIC; |
| int intr = MUIC_INTR_DETACH; |
| int vbvolt = 0; |
| int val1, val2, val3, adc; |
| |
| val1 = pmuic->vps.s.val1; |
| val2 = pmuic->vps.s.val2; |
| val3 = pmuic->vps.s.val3; |
| adc = pmuic->vps.s.adc; |
| vbvolt = pmuic->vps.s.vbvolt; |
| |
| /* Attached */ |
| switch (val1) { |
| case DEV_TYPE1_CDP: |
| intr = MUIC_INTR_ATTACH; |
| new_dev = ATTACHED_DEV_CDP_MUIC; |
| pr_info("%s : USB_CDP DETECTED\n", MUIC_DEV_NAME); |
| break; |
| case DEV_TYPE1_USB: |
| intr = MUIC_INTR_ATTACH; |
| new_dev = ATTACHED_DEV_USB_MUIC; |
| pr_info("%s : USB DETECTED\n", MUIC_DEV_NAME); |
| break; |
| case DEV_TYPE1_DEDICATED_CHG: |
| intr = MUIC_INTR_ATTACH; |
| new_dev = ATTACHED_DEV_TA_MUIC; |
| pr_info("%s : DEDICATED CHARGER DETECTED\n", MUIC_DEV_NAME); |
| break; |
| case DEV_TYPE1_USB_OTG: |
| intr = MUIC_INTR_ATTACH; |
| new_dev = ATTACHED_DEV_OTG_MUIC; |
| pr_info("%s : USB_OTG DETECTED\n", MUIC_DEV_NAME); |
| break; |
| case DEV_TYPE1_AUDIO_2: |
| intr = MUIC_INTR_ATTACH; |
| new_dev = ATTACHED_DEV_USB_LANHUB_MUIC; |
| pr_info("%s : LANHUB DETECTED\n", MUIC_DEV_NAME); |
| break; |
| |
| default: |
| break; |
| } |
| |
| switch (val2) { |
| case DEV_TYPE2_JIG_UART_OFF: |
| intr = MUIC_INTR_ATTACH; |
| new_dev = ATTACHED_DEV_JIG_UART_OFF_MUIC; |
| pr_info("%s : JIG_UART_OFF DETECTED\n", MUIC_DEV_NAME); |
| break; |
| case DEV_TYPE2_JIG_USB_OFF: |
| if (!vbvolt) break; |
| intr = MUIC_INTR_ATTACH; |
| new_dev = ATTACHED_DEV_JIG_USB_OFF_MUIC; |
| pr_info("%s : JIG_USB_OFF DETECTED\n", MUIC_DEV_NAME); |
| break; |
| case DEV_TYPE2_JIG_USB_ON: |
| if (!vbvolt) break; |
| intr = MUIC_INTR_ATTACH; |
| new_dev = ATTACHED_DEV_JIG_USB_ON_MUIC; |
| pr_info("%s : JIG_USB_ON DETECTED\n", MUIC_DEV_NAME); |
| break; |
| case DEV_TYPE2_TTY: |
| /* MM-dock is handled in an attachment function with vbus */ |
| intr = MUIC_INTR_ATTACH; |
| new_dev = ATTACHED_DEV_UNIVERSAL_MMDOCK_MUIC; |
| pr_info("%s : UNIVERSAL_MMDOCK DETECTED(%d)\n", MUIC_DEV_NAME, vbvolt); |
| break; |
| |
| default: |
| break; |
| } |
| |
| if (val3 & DEV_TYPE3_CHG_TYPE) |
| { |
| intr = MUIC_INTR_ATTACH; |
| |
| if (val3 & DEV_TYPE3_NO_STD_CHG) { |
| new_dev = ATTACHED_DEV_USB_MUIC; |
| pr_info("%s : TYPE3 DCD_OUT_TIMEOUT DETECTED\n", MUIC_DEV_NAME); |
| |
| } else { |
| new_dev = ATTACHED_DEV_TA_MUIC; |
| pr_info("%s : TYPE3_CHARGER DETECTED\n", MUIC_DEV_NAME); |
| } |
| } |
| |
| if (val2 & DEV_TYPE2_AV || val3 & DEV_TYPE3_AV_WITH_VBUS) |
| { |
| intr = MUIC_INTR_ATTACH; |
| new_dev = ATTACHED_DEV_DESKDOCK_MUIC; |
| pr_info("%s : DESKDOCK DETECTED\n", MUIC_DEV_NAME); |
| } |
| |
| if (val3 & DEV_TYPE3_MHL) |
| { |
| intr = MUIC_INTR_ATTACH; |
| new_dev = ATTACHED_DEV_MHL_MUIC; |
| pr_info("%s : MHL DETECTED\n", MUIC_DEV_NAME); |
| } |
| |
| /* If there is no matching device found using device type registers |
| use ADC to find the attached device */ |
| if(new_dev == ATTACHED_DEV_UNKNOWN_MUIC) { |
| switch (adc) { |
| case ADC_CEA936ATYPE1_CHG : /*200k ohm */ |
| { |
| /* For LG USB cable which has 219k ohm ID */ |
| int rescanned_dev = do_BCD_rescan(pmuic); |
| |
| if (rescanned_dev > 0) { |
| pr_info("%s : TYPE1 CHARGER DETECTED(USB)\n", MUIC_DEV_NAME); |
| intr = MUIC_INTR_ATTACH; |
| new_dev = rescanned_dev; |
| } |
| break; |
| } |
| case ADC_CEA936ATYPE2_CHG: |
| intr = MUIC_INTR_ATTACH; |
| new_dev = ATTACHED_DEV_TA_MUIC; |
| pr_info("%s : TYPE1/2 CHARGER DETECTED(TA)\n", MUIC_DEV_NAME); |
| break; |
| case ADC_JIG_USB_OFF: /* 255k */ |
| if (!vbvolt) break; |
| if (new_dev != ATTACHED_DEV_JIG_USB_OFF_MUIC) { |
| intr = MUIC_INTR_ATTACH; |
| new_dev = ATTACHED_DEV_JIG_USB_OFF_MUIC; |
| pr_info("%s : ADC JIG_USB_OFF DETECTED\n", MUIC_DEV_NAME); |
| } |
| break; |
| case ADC_JIG_USB_ON: |
| if (!vbvolt) break; |
| if (new_dev != ATTACHED_DEV_JIG_USB_ON_MUIC) { |
| intr = MUIC_INTR_ATTACH; |
| new_dev = ATTACHED_DEV_JIG_USB_ON_MUIC; |
| pr_info("%s : ADC JIG_USB_ON DETECTED\n", MUIC_DEV_NAME); |
| } |
| break; |
| case ADC_JIG_UART_OFF: |
| if (new_dev != ATTACHED_DEV_JIG_UART_OFF_MUIC) { |
| intr = MUIC_INTR_ATTACH; |
| new_dev = ATTACHED_DEV_JIG_UART_OFF_MUIC; |
| pr_info("%s : ADC JIG_UART_OFF DETECTED\n", MUIC_DEV_NAME); |
| } |
| break; |
| case ADC_JIG_UART_ON: |
| /* This is the mode to wake up device during factory mode. |
| * This device type SHOULD be handled in muic_state.c to |
| * support both factory & rustproof mode. |
| */ |
| if (new_dev != ATTACHED_DEV_JIG_UART_ON_MUIC) { |
| intr = MUIC_INTR_ATTACH; |
| new_dev = ATTACHED_DEV_JIG_UART_ON_MUIC; |
| pr_info("%s : ADC JIG_UART_ON DETECTED\n", MUIC_DEV_NAME); |
| } |
| break; |
| #ifdef CONFIG_MUIC_SM5703_SUPPORT_AUDIODOCK |
| case ADC_AUDIODOCK: |
| intr = MUIC_INTR_ATTACH; |
| new_dev = ATTACHED_DEV_AUDIODOCK_MUIC; |
| pr_info("%s : ADC AUDIODOCK DETECTED\n", MUIC_DEV_NAME); |
| break; |
| #endif |
| case ADC_CHARGING_CABLE: |
| intr = MUIC_INTR_ATTACH; |
| new_dev = ATTACHED_DEV_CHARGING_CABLE_MUIC; |
| pr_info("%s : PS_CABLE DETECTED\n", MUIC_DEV_NAME); |
| break; |
| case ADC_OPEN: |
| /* sometimes muic fails to catch JIG_UART_OFF detaching */ |
| /* double check with ADC */ |
| if (new_dev == ATTACHED_DEV_JIG_UART_OFF_MUIC) { |
| new_dev = ATTACHED_DEV_UNKNOWN_MUIC; |
| intr = MUIC_INTR_DETACH; |
| pr_info("%s : ADC OPEN DETECTED\n", MUIC_DEV_NAME); |
| } |
| break; |
| case ADC_GAMEPAD: |
| pr_info("%s : ADC GAMEPAD Discarded\n", MUIC_DEV_NAME); |
| break; |
| |
| case ADC_RESERVED_VZW: |
| new_dev = ATTACHED_DEV_VZW_ACC_MUIC; |
| intr = MUIC_INTR_ATTACH; |
| pr_info("%s : ADC VZW_ACC DETECTED\n", MUIC_DEV_NAME); |
| break; |
| |
| case ADC_INCOMPATIBLE_VZW: |
| new_dev = ATTACHED_DEV_VZW_INCOMPATIBLE_MUIC; |
| intr = MUIC_INTR_ATTACH; |
| pr_info("%s : ADC INCOMPATIBLE_VZW DETECTED\n", MUIC_DEV_NAME); |
| break; |
| |
| default: |
| pr_warn("%s:%s unsupported ADC(0x%02x)\n", MUIC_DEV_NAME, |
| __func__, adc); |
| if(vbvolt) { |
| intr = MUIC_INTR_ATTACH; |
| new_dev = ATTACHED_DEV_UNDEFINED_CHARGING_MUIC; |
| pr_info("%s : UNDEFINED VB DETECTED\n", MUIC_DEV_NAME); |
| } else |
| intr = MUIC_INTR_DETACH; |
| break; |
| } |
| } |
| |
| /* Check if the cable type is supported. |
| */ |
| if (vps_is_supported_dev(new_dev)) |
| pr_info("%s:Supported.\n", __func__); |
| else if(vbvolt && (intr == MUIC_INTR_ATTACH)) { |
| new_dev = ATTACHED_DEV_UNDEFINED_CHARGING_MUIC; |
| pr_info("%s:Unsupported->UNDEFINED_CHARGING\n", __func__); |
| } else { |
| intr = MUIC_INTR_DETACH; |
| new_dev = ATTACHED_DEV_UNKNOWN_MUIC; |
| pr_info("%s:Unsupported->Discarded.\n", __func__); |
| } |
| |
| *pintr = intr; |
| *pdev = new_dev; |
| |
| return 0; |
| } |
| |
| int vps_resolve_dev(muic_data_t *pmuic, muic_attached_dev_t *pdev, int *pintr) |
| { |
| if(pmuic->vps_table == VPS_TYPE_TABLE) |
| return vps_find_attached_dev(pmuic,pdev,pintr); |
| else |
| return resolve_dedicated_dev(pmuic, pdev, pintr); |
| } |