1*27a28d32SJaewon Kim /* 2*27a28d32SJaewon Kim * extcon-max77843.c - Maxim MAX77843 extcon driver to support 3*27a28d32SJaewon Kim * MUIC(Micro USB Interface Controller) 4*27a28d32SJaewon Kim * 5*27a28d32SJaewon Kim * Copyright (C) 2015 Samsung Electronics 6*27a28d32SJaewon Kim * Author: Jaewon Kim <jaewon02.kim@samsung.com> 7*27a28d32SJaewon Kim * 8*27a28d32SJaewon Kim * This program is free software; you can redistribute it and/or modify 9*27a28d32SJaewon Kim * it under the terms of the GNU General Public License as published by 10*27a28d32SJaewon Kim * the Free Software Foundation; either version 2 of the License, or 11*27a28d32SJaewon Kim * (at your option) any later version. 12*27a28d32SJaewon Kim */ 13*27a28d32SJaewon Kim 14*27a28d32SJaewon Kim #include <linux/extcon.h> 15*27a28d32SJaewon Kim #include <linux/i2c.h> 16*27a28d32SJaewon Kim #include <linux/interrupt.h> 17*27a28d32SJaewon Kim #include <linux/kernel.h> 18*27a28d32SJaewon Kim #include <linux/mfd/max77843-private.h> 19*27a28d32SJaewon Kim #include <linux/module.h> 20*27a28d32SJaewon Kim #include <linux/platform_device.h> 21*27a28d32SJaewon Kim #include <linux/workqueue.h> 22*27a28d32SJaewon Kim 23*27a28d32SJaewon Kim #define DELAY_MS_DEFAULT 15000 /* unit: millisecond */ 24*27a28d32SJaewon Kim 25*27a28d32SJaewon Kim enum max77843_muic_status { 26*27a28d32SJaewon Kim MAX77843_MUIC_STATUS1 = 0, 27*27a28d32SJaewon Kim MAX77843_MUIC_STATUS2, 28*27a28d32SJaewon Kim MAX77843_MUIC_STATUS3, 29*27a28d32SJaewon Kim 30*27a28d32SJaewon Kim MAX77843_MUIC_STATUS_NUM, 31*27a28d32SJaewon Kim }; 32*27a28d32SJaewon Kim 33*27a28d32SJaewon Kim struct max77843_muic_info { 34*27a28d32SJaewon Kim struct device *dev; 35*27a28d32SJaewon Kim struct max77843 *max77843; 36*27a28d32SJaewon Kim struct extcon_dev *edev; 37*27a28d32SJaewon Kim 38*27a28d32SJaewon Kim struct mutex mutex; 39*27a28d32SJaewon Kim struct work_struct irq_work; 40*27a28d32SJaewon Kim struct delayed_work wq_detcable; 41*27a28d32SJaewon Kim 42*27a28d32SJaewon Kim u8 status[MAX77843_MUIC_STATUS_NUM]; 43*27a28d32SJaewon Kim int prev_cable_type; 44*27a28d32SJaewon Kim int prev_chg_type; 45*27a28d32SJaewon Kim int prev_gnd_type; 46*27a28d32SJaewon Kim 47*27a28d32SJaewon Kim bool irq_adc; 48*27a28d32SJaewon Kim bool irq_chg; 49*27a28d32SJaewon Kim }; 50*27a28d32SJaewon Kim 51*27a28d32SJaewon Kim enum max77843_muic_cable_group { 52*27a28d32SJaewon Kim MAX77843_CABLE_GROUP_ADC = 0, 53*27a28d32SJaewon Kim MAX77843_CABLE_GROUP_ADC_GND, 54*27a28d32SJaewon Kim MAX77843_CABLE_GROUP_CHG, 55*27a28d32SJaewon Kim }; 56*27a28d32SJaewon Kim 57*27a28d32SJaewon Kim enum max77843_muic_adc_debounce_time { 58*27a28d32SJaewon Kim MAX77843_DEBOUNCE_TIME_5MS = 0, 59*27a28d32SJaewon Kim MAX77843_DEBOUNCE_TIME_10MS, 60*27a28d32SJaewon Kim MAX77843_DEBOUNCE_TIME_25MS, 61*27a28d32SJaewon Kim MAX77843_DEBOUNCE_TIME_38_62MS, 62*27a28d32SJaewon Kim }; 63*27a28d32SJaewon Kim 64*27a28d32SJaewon Kim /* Define accessory cable type */ 65*27a28d32SJaewon Kim enum max77843_muic_accessory_type { 66*27a28d32SJaewon Kim MAX77843_MUIC_ADC_GROUND = 0, 67*27a28d32SJaewon Kim MAX77843_MUIC_ADC_SEND_END_BUTTON, 68*27a28d32SJaewon Kim MAX77843_MUIC_ADC_REMOTE_S1_BUTTON, 69*27a28d32SJaewon Kim MAX77843_MUIC_ADC_REMOTE_S2_BUTTON, 70*27a28d32SJaewon Kim MAX77843_MUIC_ADC_REMOTE_S3_BUTTON, 71*27a28d32SJaewon Kim MAX77843_MUIC_ADC_REMOTE_S4_BUTTON, 72*27a28d32SJaewon Kim MAX77843_MUIC_ADC_REMOTE_S5_BUTTON, 73*27a28d32SJaewon Kim MAX77843_MUIC_ADC_REMOTE_S6_BUTTON, 74*27a28d32SJaewon Kim MAX77843_MUIC_ADC_REMOTE_S7_BUTTON, 75*27a28d32SJaewon Kim MAX77843_MUIC_ADC_REMOTE_S8_BUTTON, 76*27a28d32SJaewon Kim MAX77843_MUIC_ADC_REMOTE_S9_BUTTON, 77*27a28d32SJaewon Kim MAX77843_MUIC_ADC_REMOTE_S10_BUTTON, 78*27a28d32SJaewon Kim MAX77843_MUIC_ADC_REMOTE_S11_BUTTON, 79*27a28d32SJaewon Kim MAX77843_MUIC_ADC_REMOTE_S12_BUTTON, 80*27a28d32SJaewon Kim MAX77843_MUIC_ADC_RESERVED_ACC_1, 81*27a28d32SJaewon Kim MAX77843_MUIC_ADC_RESERVED_ACC_2, 82*27a28d32SJaewon Kim MAX77843_MUIC_ADC_RESERVED_ACC_3, 83*27a28d32SJaewon Kim MAX77843_MUIC_ADC_RESERVED_ACC_4, 84*27a28d32SJaewon Kim MAX77843_MUIC_ADC_RESERVED_ACC_5, 85*27a28d32SJaewon Kim MAX77843_MUIC_ADC_AUDIO_DEVICE_TYPE2, 86*27a28d32SJaewon Kim MAX77843_MUIC_ADC_PHONE_POWERED_DEV, 87*27a28d32SJaewon Kim MAX77843_MUIC_ADC_TTY_CONVERTER, 88*27a28d32SJaewon Kim MAX77843_MUIC_ADC_UART_CABLE, 89*27a28d32SJaewon Kim MAX77843_MUIC_ADC_CEA936A_TYPE1_CHG, 90*27a28d32SJaewon Kim MAX77843_MUIC_ADC_FACTORY_MODE_USB_OFF, 91*27a28d32SJaewon Kim MAX77843_MUIC_ADC_FACTORY_MODE_USB_ON, 92*27a28d32SJaewon Kim MAX77843_MUIC_ADC_AV_CABLE_NOLOAD, 93*27a28d32SJaewon Kim MAX77843_MUIC_ADC_CEA936A_TYPE2_CHG, 94*27a28d32SJaewon Kim MAX77843_MUIC_ADC_FACTORY_MODE_UART_OFF, 95*27a28d32SJaewon Kim MAX77843_MUIC_ADC_FACTORY_MODE_UART_ON, 96*27a28d32SJaewon Kim MAX77843_MUIC_ADC_AUDIO_DEVICE_TYPE1, 97*27a28d32SJaewon Kim MAX77843_MUIC_ADC_OPEN, 98*27a28d32SJaewon Kim 99*27a28d32SJaewon Kim /* The blow accessories should check 100*27a28d32SJaewon Kim not only ADC value but also ADC1K and VBVolt value. */ 101*27a28d32SJaewon Kim /* Offset|ADC1K|VBVolt| */ 102*27a28d32SJaewon Kim MAX77843_MUIC_GND_USB_HOST = 0x100, /* 0x1| 0| 0| */ 103*27a28d32SJaewon Kim MAX77843_MUIC_GND_USB_HOST_VB = 0x101, /* 0x1| 0| 1| */ 104*27a28d32SJaewon Kim MAX77843_MUIC_GND_MHL = 0x102, /* 0x1| 1| 0| */ 105*27a28d32SJaewon Kim MAX77843_MUIC_GND_MHL_VB = 0x103, /* 0x1| 1| 1| */ 106*27a28d32SJaewon Kim }; 107*27a28d32SJaewon Kim 108*27a28d32SJaewon Kim /* Define charger cable type */ 109*27a28d32SJaewon Kim enum max77843_muic_charger_type { 110*27a28d32SJaewon Kim MAX77843_MUIC_CHG_NONE = 0, 111*27a28d32SJaewon Kim MAX77843_MUIC_CHG_USB, 112*27a28d32SJaewon Kim MAX77843_MUIC_CHG_DOWNSTREAM, 113*27a28d32SJaewon Kim MAX77843_MUIC_CHG_DEDICATED, 114*27a28d32SJaewon Kim MAX77843_MUIC_CHG_SPECIAL_500MA, 115*27a28d32SJaewon Kim MAX77843_MUIC_CHG_SPECIAL_1A, 116*27a28d32SJaewon Kim MAX77843_MUIC_CHG_SPECIAL_BIAS, 117*27a28d32SJaewon Kim MAX77843_MUIC_CHG_RESERVED, 118*27a28d32SJaewon Kim MAX77843_MUIC_CHG_GND, 119*27a28d32SJaewon Kim }; 120*27a28d32SJaewon Kim 121*27a28d32SJaewon Kim enum { 122*27a28d32SJaewon Kim MAX77843_CABLE_USB = 0, 123*27a28d32SJaewon Kim MAX77843_CABLE_USB_HOST, 124*27a28d32SJaewon Kim MAX77843_CABLE_TA, 125*27a28d32SJaewon Kim MAX77843_CABLE_CHARGE_DOWNSTREAM, 126*27a28d32SJaewon Kim MAX77843_CABLE_FAST_CHARGER, 127*27a28d32SJaewon Kim MAX77843_CABLE_SLOW_CHARGER, 128*27a28d32SJaewon Kim MAX77843_CABLE_MHL, 129*27a28d32SJaewon Kim MAX77843_CABLE_MHL_TA, 130*27a28d32SJaewon Kim MAX77843_CABLE_JIG_USB_ON, 131*27a28d32SJaewon Kim MAX77843_CABLE_JIG_USB_OFF, 132*27a28d32SJaewon Kim MAX77843_CABLE_JIG_UART_ON, 133*27a28d32SJaewon Kim MAX77843_CABLE_JIG_UART_OFF, 134*27a28d32SJaewon Kim 135*27a28d32SJaewon Kim MAX77843_CABLE_NUM, 136*27a28d32SJaewon Kim }; 137*27a28d32SJaewon Kim 138*27a28d32SJaewon Kim static const char *max77843_extcon_cable[] = { 139*27a28d32SJaewon Kim [MAX77843_CABLE_USB] = "USB", 140*27a28d32SJaewon Kim [MAX77843_CABLE_USB_HOST] = "USB-HOST", 141*27a28d32SJaewon Kim [MAX77843_CABLE_TA] = "TA", 142*27a28d32SJaewon Kim [MAX77843_CABLE_CHARGE_DOWNSTREAM] = "CHARGER-DOWNSTREAM", 143*27a28d32SJaewon Kim [MAX77843_CABLE_FAST_CHARGER] = "FAST-CHARGER", 144*27a28d32SJaewon Kim [MAX77843_CABLE_SLOW_CHARGER] = "SLOW-CHARGER", 145*27a28d32SJaewon Kim [MAX77843_CABLE_MHL] = "MHL", 146*27a28d32SJaewon Kim [MAX77843_CABLE_MHL_TA] = "MHL-TA", 147*27a28d32SJaewon Kim [MAX77843_CABLE_JIG_USB_ON] = "JIG-USB-ON", 148*27a28d32SJaewon Kim [MAX77843_CABLE_JIG_USB_OFF] = "JIG-USB-OFF", 149*27a28d32SJaewon Kim [MAX77843_CABLE_JIG_UART_ON] = "JIG-UART-ON", 150*27a28d32SJaewon Kim [MAX77843_CABLE_JIG_UART_OFF] = "JIG-UART-OFF", 151*27a28d32SJaewon Kim }; 152*27a28d32SJaewon Kim 153*27a28d32SJaewon Kim struct max77843_muic_irq { 154*27a28d32SJaewon Kim unsigned int irq; 155*27a28d32SJaewon Kim const char *name; 156*27a28d32SJaewon Kim unsigned int virq; 157*27a28d32SJaewon Kim }; 158*27a28d32SJaewon Kim 159*27a28d32SJaewon Kim static struct max77843_muic_irq max77843_muic_irqs[] = { 160*27a28d32SJaewon Kim { MAX77843_MUIC_IRQ_INT1_ADC, "MUIC-ADC" }, 161*27a28d32SJaewon Kim { MAX77843_MUIC_IRQ_INT1_ADCERROR, "MUIC-ADC_ERROR" }, 162*27a28d32SJaewon Kim { MAX77843_MUIC_IRQ_INT1_ADC1K, "MUIC-ADC1K" }, 163*27a28d32SJaewon Kim { MAX77843_MUIC_IRQ_INT2_CHGTYP, "MUIC-CHGTYP" }, 164*27a28d32SJaewon Kim { MAX77843_MUIC_IRQ_INT2_CHGDETRUN, "MUIC-CHGDETRUN" }, 165*27a28d32SJaewon Kim { MAX77843_MUIC_IRQ_INT2_DCDTMR, "MUIC-DCDTMR" }, 166*27a28d32SJaewon Kim { MAX77843_MUIC_IRQ_INT2_DXOVP, "MUIC-DXOVP" }, 167*27a28d32SJaewon Kim { MAX77843_MUIC_IRQ_INT2_VBVOLT, "MUIC-VBVOLT" }, 168*27a28d32SJaewon Kim { MAX77843_MUIC_IRQ_INT3_VBADC, "MUIC-VBADC" }, 169*27a28d32SJaewon Kim { MAX77843_MUIC_IRQ_INT3_VDNMON, "MUIC-VDNMON" }, 170*27a28d32SJaewon Kim { MAX77843_MUIC_IRQ_INT3_DNRES, "MUIC-DNRES" }, 171*27a28d32SJaewon Kim { MAX77843_MUIC_IRQ_INT3_MPNACK, "MUIC-MPNACK"}, 172*27a28d32SJaewon Kim { MAX77843_MUIC_IRQ_INT3_MRXBUFOW, "MUIC-MRXBUFOW"}, 173*27a28d32SJaewon Kim { MAX77843_MUIC_IRQ_INT3_MRXTRF, "MUIC-MRXTRF"}, 174*27a28d32SJaewon Kim { MAX77843_MUIC_IRQ_INT3_MRXPERR, "MUIC-MRXPERR"}, 175*27a28d32SJaewon Kim { MAX77843_MUIC_IRQ_INT3_MRXRDY, "MUIC-MRXRDY"}, 176*27a28d32SJaewon Kim }; 177*27a28d32SJaewon Kim 178*27a28d32SJaewon Kim static const struct regmap_config max77843_muic_regmap_config = { 179*27a28d32SJaewon Kim .reg_bits = 8, 180*27a28d32SJaewon Kim .val_bits = 8, 181*27a28d32SJaewon Kim .max_register = MAX77843_MUIC_REG_END, 182*27a28d32SJaewon Kim }; 183*27a28d32SJaewon Kim 184*27a28d32SJaewon Kim static const struct regmap_irq max77843_muic_irq[] = { 185*27a28d32SJaewon Kim /* INT1 interrupt */ 186*27a28d32SJaewon Kim { .reg_offset = 0, .mask = MAX77843_MUIC_ADC, }, 187*27a28d32SJaewon Kim { .reg_offset = 0, .mask = MAX77843_MUIC_ADCERROR, }, 188*27a28d32SJaewon Kim { .reg_offset = 0, .mask = MAX77843_MUIC_ADC1K, }, 189*27a28d32SJaewon Kim 190*27a28d32SJaewon Kim /* INT2 interrupt */ 191*27a28d32SJaewon Kim { .reg_offset = 1, .mask = MAX77843_MUIC_CHGTYP, }, 192*27a28d32SJaewon Kim { .reg_offset = 1, .mask = MAX77843_MUIC_CHGDETRUN, }, 193*27a28d32SJaewon Kim { .reg_offset = 1, .mask = MAX77843_MUIC_DCDTMR, }, 194*27a28d32SJaewon Kim { .reg_offset = 1, .mask = MAX77843_MUIC_DXOVP, }, 195*27a28d32SJaewon Kim { .reg_offset = 1, .mask = MAX77843_MUIC_VBVOLT, }, 196*27a28d32SJaewon Kim 197*27a28d32SJaewon Kim /* INT3 interrupt */ 198*27a28d32SJaewon Kim { .reg_offset = 2, .mask = MAX77843_MUIC_VBADC, }, 199*27a28d32SJaewon Kim { .reg_offset = 2, .mask = MAX77843_MUIC_VDNMON, }, 200*27a28d32SJaewon Kim { .reg_offset = 2, .mask = MAX77843_MUIC_DNRES, }, 201*27a28d32SJaewon Kim { .reg_offset = 2, .mask = MAX77843_MUIC_MPNACK, }, 202*27a28d32SJaewon Kim { .reg_offset = 2, .mask = MAX77843_MUIC_MRXBUFOW, }, 203*27a28d32SJaewon Kim { .reg_offset = 2, .mask = MAX77843_MUIC_MRXTRF, }, 204*27a28d32SJaewon Kim { .reg_offset = 2, .mask = MAX77843_MUIC_MRXPERR, }, 205*27a28d32SJaewon Kim { .reg_offset = 2, .mask = MAX77843_MUIC_MRXRDY, }, 206*27a28d32SJaewon Kim }; 207*27a28d32SJaewon Kim 208*27a28d32SJaewon Kim static const struct regmap_irq_chip max77843_muic_irq_chip = { 209*27a28d32SJaewon Kim .name = "max77843-muic", 210*27a28d32SJaewon Kim .status_base = MAX77843_MUIC_REG_INT1, 211*27a28d32SJaewon Kim .mask_base = MAX77843_MUIC_REG_INTMASK1, 212*27a28d32SJaewon Kim .mask_invert = true, 213*27a28d32SJaewon Kim .num_regs = 3, 214*27a28d32SJaewon Kim .irqs = max77843_muic_irq, 215*27a28d32SJaewon Kim .num_irqs = ARRAY_SIZE(max77843_muic_irq), 216*27a28d32SJaewon Kim }; 217*27a28d32SJaewon Kim 218*27a28d32SJaewon Kim static int max77843_muic_set_path(struct max77843_muic_info *info, 219*27a28d32SJaewon Kim u8 val, bool attached) 220*27a28d32SJaewon Kim { 221*27a28d32SJaewon Kim struct max77843 *max77843 = info->max77843; 222*27a28d32SJaewon Kim int ret = 0; 223*27a28d32SJaewon Kim unsigned int ctrl1, ctrl2; 224*27a28d32SJaewon Kim 225*27a28d32SJaewon Kim if (attached) 226*27a28d32SJaewon Kim ctrl1 = val; 227*27a28d32SJaewon Kim else 228*27a28d32SJaewon Kim ctrl1 = CONTROL1_SW_OPEN; 229*27a28d32SJaewon Kim 230*27a28d32SJaewon Kim ret = regmap_update_bits(max77843->regmap_muic, 231*27a28d32SJaewon Kim MAX77843_MUIC_REG_CONTROL1, 232*27a28d32SJaewon Kim CONTROL1_COM_SW, ctrl1); 233*27a28d32SJaewon Kim if (ret < 0) { 234*27a28d32SJaewon Kim dev_err(info->dev, "Cannot switch MUIC port\n"); 235*27a28d32SJaewon Kim return ret; 236*27a28d32SJaewon Kim } 237*27a28d32SJaewon Kim 238*27a28d32SJaewon Kim if (attached) 239*27a28d32SJaewon Kim ctrl2 = MAX77843_MUIC_CONTROL2_CPEN_MASK; 240*27a28d32SJaewon Kim else 241*27a28d32SJaewon Kim ctrl2 = MAX77843_MUIC_CONTROL2_LOWPWR_MASK; 242*27a28d32SJaewon Kim 243*27a28d32SJaewon Kim ret = regmap_update_bits(max77843->regmap_muic, 244*27a28d32SJaewon Kim MAX77843_MUIC_REG_CONTROL2, 245*27a28d32SJaewon Kim MAX77843_MUIC_CONTROL2_LOWPWR_MASK | 246*27a28d32SJaewon Kim MAX77843_MUIC_CONTROL2_CPEN_MASK, ctrl2); 247*27a28d32SJaewon Kim if (ret < 0) { 248*27a28d32SJaewon Kim dev_err(info->dev, "Cannot update lowpower mode\n"); 249*27a28d32SJaewon Kim return ret; 250*27a28d32SJaewon Kim } 251*27a28d32SJaewon Kim 252*27a28d32SJaewon Kim dev_dbg(info->dev, 253*27a28d32SJaewon Kim "CONTROL1 : 0x%02x, CONTROL2 : 0x%02x, state : %s\n", 254*27a28d32SJaewon Kim ctrl1, ctrl2, attached ? "attached" : "detached"); 255*27a28d32SJaewon Kim 256*27a28d32SJaewon Kim return 0; 257*27a28d32SJaewon Kim } 258*27a28d32SJaewon Kim 259*27a28d32SJaewon Kim static int max77843_muic_get_cable_type(struct max77843_muic_info *info, 260*27a28d32SJaewon Kim enum max77843_muic_cable_group group, bool *attached) 261*27a28d32SJaewon Kim { 262*27a28d32SJaewon Kim int adc, chg_type, cable_type, gnd_type; 263*27a28d32SJaewon Kim 264*27a28d32SJaewon Kim adc = info->status[MAX77843_MUIC_STATUS1] & 265*27a28d32SJaewon Kim MAX77843_MUIC_STATUS1_ADC_MASK; 266*27a28d32SJaewon Kim adc >>= STATUS1_ADC_SHIFT; 267*27a28d32SJaewon Kim 268*27a28d32SJaewon Kim switch (group) { 269*27a28d32SJaewon Kim case MAX77843_CABLE_GROUP_ADC: 270*27a28d32SJaewon Kim if (adc == MAX77843_MUIC_ADC_OPEN) { 271*27a28d32SJaewon Kim *attached = false; 272*27a28d32SJaewon Kim cable_type = info->prev_cable_type; 273*27a28d32SJaewon Kim info->prev_cable_type = MAX77843_MUIC_ADC_OPEN; 274*27a28d32SJaewon Kim } else { 275*27a28d32SJaewon Kim *attached = true; 276*27a28d32SJaewon Kim cable_type = info->prev_cable_type = adc; 277*27a28d32SJaewon Kim } 278*27a28d32SJaewon Kim break; 279*27a28d32SJaewon Kim case MAX77843_CABLE_GROUP_CHG: 280*27a28d32SJaewon Kim chg_type = info->status[MAX77843_MUIC_STATUS2] & 281*27a28d32SJaewon Kim MAX77843_MUIC_STATUS2_CHGTYP_MASK; 282*27a28d32SJaewon Kim 283*27a28d32SJaewon Kim /* Check GROUND accessory with charger cable */ 284*27a28d32SJaewon Kim if (adc == MAX77843_MUIC_ADC_GROUND) { 285*27a28d32SJaewon Kim if (chg_type == MAX77843_MUIC_CHG_NONE) { 286*27a28d32SJaewon Kim /* The following state when charger cable is 287*27a28d32SJaewon Kim * disconnected but the GROUND accessory still 288*27a28d32SJaewon Kim * connected */ 289*27a28d32SJaewon Kim *attached = false; 290*27a28d32SJaewon Kim cable_type = info->prev_chg_type; 291*27a28d32SJaewon Kim info->prev_chg_type = MAX77843_MUIC_CHG_NONE; 292*27a28d32SJaewon Kim } else { 293*27a28d32SJaewon Kim 294*27a28d32SJaewon Kim /* The following state when charger cable is 295*27a28d32SJaewon Kim * connected on the GROUND accessory */ 296*27a28d32SJaewon Kim *attached = true; 297*27a28d32SJaewon Kim cable_type = MAX77843_MUIC_CHG_GND; 298*27a28d32SJaewon Kim info->prev_chg_type = MAX77843_MUIC_CHG_GND; 299*27a28d32SJaewon Kim } 300*27a28d32SJaewon Kim break; 301*27a28d32SJaewon Kim } 302*27a28d32SJaewon Kim 303*27a28d32SJaewon Kim if (chg_type == MAX77843_MUIC_CHG_NONE) { 304*27a28d32SJaewon Kim *attached = false; 305*27a28d32SJaewon Kim cable_type = info->prev_chg_type; 306*27a28d32SJaewon Kim info->prev_chg_type = MAX77843_MUIC_CHG_NONE; 307*27a28d32SJaewon Kim } else { 308*27a28d32SJaewon Kim *attached = true; 309*27a28d32SJaewon Kim cable_type = info->prev_chg_type = chg_type; 310*27a28d32SJaewon Kim } 311*27a28d32SJaewon Kim break; 312*27a28d32SJaewon Kim case MAX77843_CABLE_GROUP_ADC_GND: 313*27a28d32SJaewon Kim if (adc == MAX77843_MUIC_ADC_OPEN) { 314*27a28d32SJaewon Kim *attached = false; 315*27a28d32SJaewon Kim cable_type = info->prev_gnd_type; 316*27a28d32SJaewon Kim info->prev_gnd_type = MAX77843_MUIC_ADC_OPEN; 317*27a28d32SJaewon Kim } else { 318*27a28d32SJaewon Kim *attached = true; 319*27a28d32SJaewon Kim 320*27a28d32SJaewon Kim /* Offset|ADC1K|VBVolt| 321*27a28d32SJaewon Kim * 0x1| 0| 0| USB-HOST 322*27a28d32SJaewon Kim * 0x1| 0| 1| USB-HOST with VB 323*27a28d32SJaewon Kim * 0x1| 1| 0| MHL 324*27a28d32SJaewon Kim * 0x1| 1| 1| MHL with VB */ 325*27a28d32SJaewon Kim /* Get ADC1K register bit */ 326*27a28d32SJaewon Kim gnd_type = (info->status[MAX77843_MUIC_STATUS1] & 327*27a28d32SJaewon Kim MAX77843_MUIC_STATUS1_ADC1K_MASK); 328*27a28d32SJaewon Kim 329*27a28d32SJaewon Kim /* Get VBVolt register bit */ 330*27a28d32SJaewon Kim gnd_type |= (info->status[MAX77843_MUIC_STATUS2] & 331*27a28d32SJaewon Kim MAX77843_MUIC_STATUS2_VBVOLT_MASK); 332*27a28d32SJaewon Kim gnd_type >>= STATUS2_VBVOLT_SHIFT; 333*27a28d32SJaewon Kim 334*27a28d32SJaewon Kim /* Offset of GND cable */ 335*27a28d32SJaewon Kim gnd_type |= MAX77843_MUIC_GND_USB_HOST; 336*27a28d32SJaewon Kim cable_type = info->prev_gnd_type = gnd_type; 337*27a28d32SJaewon Kim } 338*27a28d32SJaewon Kim break; 339*27a28d32SJaewon Kim default: 340*27a28d32SJaewon Kim dev_err(info->dev, "Unknown cable group (%d)\n", group); 341*27a28d32SJaewon Kim cable_type = -EINVAL; 342*27a28d32SJaewon Kim break; 343*27a28d32SJaewon Kim } 344*27a28d32SJaewon Kim 345*27a28d32SJaewon Kim return cable_type; 346*27a28d32SJaewon Kim } 347*27a28d32SJaewon Kim 348*27a28d32SJaewon Kim static int max77843_muic_adc_gnd_handler(struct max77843_muic_info *info) 349*27a28d32SJaewon Kim { 350*27a28d32SJaewon Kim int ret, gnd_cable_type; 351*27a28d32SJaewon Kim bool attached; 352*27a28d32SJaewon Kim 353*27a28d32SJaewon Kim gnd_cable_type = max77843_muic_get_cable_type(info, 354*27a28d32SJaewon Kim MAX77843_CABLE_GROUP_ADC_GND, &attached); 355*27a28d32SJaewon Kim dev_dbg(info->dev, "external connector is %s (gnd:0x%02x)\n", 356*27a28d32SJaewon Kim attached ? "attached" : "detached", gnd_cable_type); 357*27a28d32SJaewon Kim 358*27a28d32SJaewon Kim switch (gnd_cable_type) { 359*27a28d32SJaewon Kim case MAX77843_MUIC_GND_USB_HOST: 360*27a28d32SJaewon Kim case MAX77843_MUIC_GND_USB_HOST_VB: 361*27a28d32SJaewon Kim ret = max77843_muic_set_path(info, CONTROL1_SW_USB, attached); 362*27a28d32SJaewon Kim if (ret < 0) 363*27a28d32SJaewon Kim return ret; 364*27a28d32SJaewon Kim 365*27a28d32SJaewon Kim extcon_set_cable_state(info->edev, "USB-HOST", attached); 366*27a28d32SJaewon Kim break; 367*27a28d32SJaewon Kim case MAX77843_MUIC_GND_MHL_VB: 368*27a28d32SJaewon Kim case MAX77843_MUIC_GND_MHL: 369*27a28d32SJaewon Kim ret = max77843_muic_set_path(info, CONTROL1_SW_OPEN, attached); 370*27a28d32SJaewon Kim if (ret < 0) 371*27a28d32SJaewon Kim return ret; 372*27a28d32SJaewon Kim 373*27a28d32SJaewon Kim extcon_set_cable_state(info->edev, "MHL", attached); 374*27a28d32SJaewon Kim break; 375*27a28d32SJaewon Kim default: 376*27a28d32SJaewon Kim dev_err(info->dev, "failed to detect %s accessory(gnd:0x%x)\n", 377*27a28d32SJaewon Kim attached ? "attached" : "detached", gnd_cable_type); 378*27a28d32SJaewon Kim return -EINVAL; 379*27a28d32SJaewon Kim } 380*27a28d32SJaewon Kim 381*27a28d32SJaewon Kim return 0; 382*27a28d32SJaewon Kim } 383*27a28d32SJaewon Kim 384*27a28d32SJaewon Kim static int max77843_muic_jig_handler(struct max77843_muic_info *info, 385*27a28d32SJaewon Kim int cable_type, bool attached) 386*27a28d32SJaewon Kim { 387*27a28d32SJaewon Kim int ret; 388*27a28d32SJaewon Kim 389*27a28d32SJaewon Kim dev_dbg(info->dev, "external connector is %s (adc:0x%02x)\n", 390*27a28d32SJaewon Kim attached ? "attached" : "detached", cable_type); 391*27a28d32SJaewon Kim 392*27a28d32SJaewon Kim switch (cable_type) { 393*27a28d32SJaewon Kim case MAX77843_MUIC_ADC_FACTORY_MODE_USB_OFF: 394*27a28d32SJaewon Kim ret = max77843_muic_set_path(info, CONTROL1_SW_USB, attached); 395*27a28d32SJaewon Kim if (ret < 0) 396*27a28d32SJaewon Kim return ret; 397*27a28d32SJaewon Kim extcon_set_cable_state(info->edev, "JIG-USB-OFF", attached); 398*27a28d32SJaewon Kim break; 399*27a28d32SJaewon Kim case MAX77843_MUIC_ADC_FACTORY_MODE_USB_ON: 400*27a28d32SJaewon Kim ret = max77843_muic_set_path(info, CONTROL1_SW_USB, attached); 401*27a28d32SJaewon Kim if (ret < 0) 402*27a28d32SJaewon Kim return ret; 403*27a28d32SJaewon Kim extcon_set_cable_state(info->edev, "JIG-USB-ON", attached); 404*27a28d32SJaewon Kim break; 405*27a28d32SJaewon Kim case MAX77843_MUIC_ADC_FACTORY_MODE_UART_OFF: 406*27a28d32SJaewon Kim ret = max77843_muic_set_path(info, CONTROL1_SW_UART, attached); 407*27a28d32SJaewon Kim if (ret < 0) 408*27a28d32SJaewon Kim return ret; 409*27a28d32SJaewon Kim extcon_set_cable_state(info->edev, "JIG-UART-OFF", attached); 410*27a28d32SJaewon Kim break; 411*27a28d32SJaewon Kim default: 412*27a28d32SJaewon Kim ret = max77843_muic_set_path(info, CONTROL1_SW_OPEN, attached); 413*27a28d32SJaewon Kim if (ret < 0) 414*27a28d32SJaewon Kim return ret; 415*27a28d32SJaewon Kim break; 416*27a28d32SJaewon Kim } 417*27a28d32SJaewon Kim 418*27a28d32SJaewon Kim return 0; 419*27a28d32SJaewon Kim } 420*27a28d32SJaewon Kim 421*27a28d32SJaewon Kim static int max77843_muic_adc_handler(struct max77843_muic_info *info) 422*27a28d32SJaewon Kim { 423*27a28d32SJaewon Kim int ret, cable_type; 424*27a28d32SJaewon Kim bool attached; 425*27a28d32SJaewon Kim 426*27a28d32SJaewon Kim cable_type = max77843_muic_get_cable_type(info, 427*27a28d32SJaewon Kim MAX77843_CABLE_GROUP_ADC, &attached); 428*27a28d32SJaewon Kim 429*27a28d32SJaewon Kim dev_dbg(info->dev, 430*27a28d32SJaewon Kim "external connector is %s (adc:0x%02x, prev_adc:0x%x)\n", 431*27a28d32SJaewon Kim attached ? "attached" : "detached", cable_type, 432*27a28d32SJaewon Kim info->prev_cable_type); 433*27a28d32SJaewon Kim 434*27a28d32SJaewon Kim switch (cable_type) { 435*27a28d32SJaewon Kim case MAX77843_MUIC_ADC_GROUND: 436*27a28d32SJaewon Kim ret = max77843_muic_adc_gnd_handler(info); 437*27a28d32SJaewon Kim if (ret < 0) 438*27a28d32SJaewon Kim return ret; 439*27a28d32SJaewon Kim break; 440*27a28d32SJaewon Kim case MAX77843_MUIC_ADC_FACTORY_MODE_USB_OFF: 441*27a28d32SJaewon Kim case MAX77843_MUIC_ADC_FACTORY_MODE_USB_ON: 442*27a28d32SJaewon Kim case MAX77843_MUIC_ADC_FACTORY_MODE_UART_OFF: 443*27a28d32SJaewon Kim ret = max77843_muic_jig_handler(info, cable_type, attached); 444*27a28d32SJaewon Kim if (ret < 0) 445*27a28d32SJaewon Kim return ret; 446*27a28d32SJaewon Kim break; 447*27a28d32SJaewon Kim case MAX77843_MUIC_ADC_SEND_END_BUTTON: 448*27a28d32SJaewon Kim case MAX77843_MUIC_ADC_REMOTE_S1_BUTTON: 449*27a28d32SJaewon Kim case MAX77843_MUIC_ADC_REMOTE_S2_BUTTON: 450*27a28d32SJaewon Kim case MAX77843_MUIC_ADC_REMOTE_S3_BUTTON: 451*27a28d32SJaewon Kim case MAX77843_MUIC_ADC_REMOTE_S4_BUTTON: 452*27a28d32SJaewon Kim case MAX77843_MUIC_ADC_REMOTE_S5_BUTTON: 453*27a28d32SJaewon Kim case MAX77843_MUIC_ADC_REMOTE_S6_BUTTON: 454*27a28d32SJaewon Kim case MAX77843_MUIC_ADC_REMOTE_S7_BUTTON: 455*27a28d32SJaewon Kim case MAX77843_MUIC_ADC_REMOTE_S8_BUTTON: 456*27a28d32SJaewon Kim case MAX77843_MUIC_ADC_REMOTE_S9_BUTTON: 457*27a28d32SJaewon Kim case MAX77843_MUIC_ADC_REMOTE_S10_BUTTON: 458*27a28d32SJaewon Kim case MAX77843_MUIC_ADC_REMOTE_S11_BUTTON: 459*27a28d32SJaewon Kim case MAX77843_MUIC_ADC_REMOTE_S12_BUTTON: 460*27a28d32SJaewon Kim case MAX77843_MUIC_ADC_RESERVED_ACC_1: 461*27a28d32SJaewon Kim case MAX77843_MUIC_ADC_RESERVED_ACC_2: 462*27a28d32SJaewon Kim case MAX77843_MUIC_ADC_RESERVED_ACC_3: 463*27a28d32SJaewon Kim case MAX77843_MUIC_ADC_RESERVED_ACC_4: 464*27a28d32SJaewon Kim case MAX77843_MUIC_ADC_RESERVED_ACC_5: 465*27a28d32SJaewon Kim case MAX77843_MUIC_ADC_AUDIO_DEVICE_TYPE2: 466*27a28d32SJaewon Kim case MAX77843_MUIC_ADC_PHONE_POWERED_DEV: 467*27a28d32SJaewon Kim case MAX77843_MUIC_ADC_TTY_CONVERTER: 468*27a28d32SJaewon Kim case MAX77843_MUIC_ADC_UART_CABLE: 469*27a28d32SJaewon Kim case MAX77843_MUIC_ADC_CEA936A_TYPE1_CHG: 470*27a28d32SJaewon Kim case MAX77843_MUIC_ADC_AV_CABLE_NOLOAD: 471*27a28d32SJaewon Kim case MAX77843_MUIC_ADC_CEA936A_TYPE2_CHG: 472*27a28d32SJaewon Kim case MAX77843_MUIC_ADC_FACTORY_MODE_UART_ON: 473*27a28d32SJaewon Kim case MAX77843_MUIC_ADC_AUDIO_DEVICE_TYPE1: 474*27a28d32SJaewon Kim case MAX77843_MUIC_ADC_OPEN: 475*27a28d32SJaewon Kim dev_err(info->dev, 476*27a28d32SJaewon Kim "accessory is %s but it isn't used (adc:0x%x)\n", 477*27a28d32SJaewon Kim attached ? "attached" : "detached", cable_type); 478*27a28d32SJaewon Kim return -EAGAIN; 479*27a28d32SJaewon Kim default: 480*27a28d32SJaewon Kim dev_err(info->dev, 481*27a28d32SJaewon Kim "failed to detect %s accessory (adc:0x%x)\n", 482*27a28d32SJaewon Kim attached ? "attached" : "detached", cable_type); 483*27a28d32SJaewon Kim return -EINVAL; 484*27a28d32SJaewon Kim } 485*27a28d32SJaewon Kim 486*27a28d32SJaewon Kim return 0; 487*27a28d32SJaewon Kim } 488*27a28d32SJaewon Kim 489*27a28d32SJaewon Kim static int max77843_muic_chg_handler(struct max77843_muic_info *info) 490*27a28d32SJaewon Kim { 491*27a28d32SJaewon Kim int ret, chg_type, gnd_type; 492*27a28d32SJaewon Kim bool attached; 493*27a28d32SJaewon Kim 494*27a28d32SJaewon Kim chg_type = max77843_muic_get_cable_type(info, 495*27a28d32SJaewon Kim MAX77843_CABLE_GROUP_CHG, &attached); 496*27a28d32SJaewon Kim 497*27a28d32SJaewon Kim dev_dbg(info->dev, 498*27a28d32SJaewon Kim "external connector is %s(chg_type:0x%x, prev_chg_type:0x%x)\n", 499*27a28d32SJaewon Kim attached ? "attached" : "detached", 500*27a28d32SJaewon Kim chg_type, info->prev_chg_type); 501*27a28d32SJaewon Kim 502*27a28d32SJaewon Kim switch (chg_type) { 503*27a28d32SJaewon Kim case MAX77843_MUIC_CHG_USB: 504*27a28d32SJaewon Kim ret = max77843_muic_set_path(info, CONTROL1_SW_USB, attached); 505*27a28d32SJaewon Kim if (ret < 0) 506*27a28d32SJaewon Kim return ret; 507*27a28d32SJaewon Kim 508*27a28d32SJaewon Kim extcon_set_cable_state(info->edev, "USB", attached); 509*27a28d32SJaewon Kim break; 510*27a28d32SJaewon Kim case MAX77843_MUIC_CHG_DOWNSTREAM: 511*27a28d32SJaewon Kim ret = max77843_muic_set_path(info, CONTROL1_SW_OPEN, attached); 512*27a28d32SJaewon Kim if (ret < 0) 513*27a28d32SJaewon Kim return ret; 514*27a28d32SJaewon Kim 515*27a28d32SJaewon Kim extcon_set_cable_state(info->edev, 516*27a28d32SJaewon Kim "CHARGER-DOWNSTREAM", attached); 517*27a28d32SJaewon Kim break; 518*27a28d32SJaewon Kim case MAX77843_MUIC_CHG_DEDICATED: 519*27a28d32SJaewon Kim ret = max77843_muic_set_path(info, CONTROL1_SW_OPEN, attached); 520*27a28d32SJaewon Kim if (ret < 0) 521*27a28d32SJaewon Kim return ret; 522*27a28d32SJaewon Kim 523*27a28d32SJaewon Kim extcon_set_cable_state(info->edev, "TA", attached); 524*27a28d32SJaewon Kim break; 525*27a28d32SJaewon Kim case MAX77843_MUIC_CHG_SPECIAL_500MA: 526*27a28d32SJaewon Kim ret = max77843_muic_set_path(info, CONTROL1_SW_OPEN, attached); 527*27a28d32SJaewon Kim if (ret < 0) 528*27a28d32SJaewon Kim return ret; 529*27a28d32SJaewon Kim 530*27a28d32SJaewon Kim extcon_set_cable_state(info->edev, "SLOW-CHAREGER", attached); 531*27a28d32SJaewon Kim break; 532*27a28d32SJaewon Kim case MAX77843_MUIC_CHG_SPECIAL_1A: 533*27a28d32SJaewon Kim ret = max77843_muic_set_path(info, CONTROL1_SW_OPEN, attached); 534*27a28d32SJaewon Kim if (ret < 0) 535*27a28d32SJaewon Kim return ret; 536*27a28d32SJaewon Kim 537*27a28d32SJaewon Kim extcon_set_cable_state(info->edev, "FAST-CHARGER", attached); 538*27a28d32SJaewon Kim break; 539*27a28d32SJaewon Kim case MAX77843_MUIC_CHG_GND: 540*27a28d32SJaewon Kim gnd_type = max77843_muic_get_cable_type(info, 541*27a28d32SJaewon Kim MAX77843_CABLE_GROUP_ADC_GND, &attached); 542*27a28d32SJaewon Kim 543*27a28d32SJaewon Kim /* Charger cable on MHL accessory is attach or detach */ 544*27a28d32SJaewon Kim if (gnd_type == MAX77843_MUIC_GND_MHL_VB) 545*27a28d32SJaewon Kim extcon_set_cable_state(info->edev, "MHL-TA", true); 546*27a28d32SJaewon Kim else if (gnd_type == MAX77843_MUIC_GND_MHL) 547*27a28d32SJaewon Kim extcon_set_cable_state(info->edev, "MHL-TA", false); 548*27a28d32SJaewon Kim break; 549*27a28d32SJaewon Kim case MAX77843_MUIC_CHG_NONE: 550*27a28d32SJaewon Kim break; 551*27a28d32SJaewon Kim default: 552*27a28d32SJaewon Kim dev_err(info->dev, 553*27a28d32SJaewon Kim "failed to detect %s accessory (chg_type:0x%x)\n", 554*27a28d32SJaewon Kim attached ? "attached" : "detached", chg_type); 555*27a28d32SJaewon Kim 556*27a28d32SJaewon Kim max77843_muic_set_path(info, CONTROL1_SW_OPEN, attached); 557*27a28d32SJaewon Kim return -EINVAL; 558*27a28d32SJaewon Kim } 559*27a28d32SJaewon Kim 560*27a28d32SJaewon Kim return 0; 561*27a28d32SJaewon Kim } 562*27a28d32SJaewon Kim 563*27a28d32SJaewon Kim static void max77843_muic_irq_work(struct work_struct *work) 564*27a28d32SJaewon Kim { 565*27a28d32SJaewon Kim struct max77843_muic_info *info = container_of(work, 566*27a28d32SJaewon Kim struct max77843_muic_info, irq_work); 567*27a28d32SJaewon Kim struct max77843 *max77843 = info->max77843; 568*27a28d32SJaewon Kim int ret = 0; 569*27a28d32SJaewon Kim 570*27a28d32SJaewon Kim mutex_lock(&info->mutex); 571*27a28d32SJaewon Kim 572*27a28d32SJaewon Kim ret = regmap_bulk_read(max77843->regmap_muic, 573*27a28d32SJaewon Kim MAX77843_MUIC_REG_STATUS1, info->status, 574*27a28d32SJaewon Kim MAX77843_MUIC_STATUS_NUM); 575*27a28d32SJaewon Kim if (ret) { 576*27a28d32SJaewon Kim dev_err(info->dev, "Cannot read STATUS registers\n"); 577*27a28d32SJaewon Kim mutex_unlock(&info->mutex); 578*27a28d32SJaewon Kim return; 579*27a28d32SJaewon Kim } 580*27a28d32SJaewon Kim 581*27a28d32SJaewon Kim if (info->irq_adc) { 582*27a28d32SJaewon Kim ret = max77843_muic_adc_handler(info); 583*27a28d32SJaewon Kim if (ret) 584*27a28d32SJaewon Kim dev_err(info->dev, "Unknown cable type\n"); 585*27a28d32SJaewon Kim info->irq_adc = false; 586*27a28d32SJaewon Kim } 587*27a28d32SJaewon Kim 588*27a28d32SJaewon Kim if (info->irq_chg) { 589*27a28d32SJaewon Kim ret = max77843_muic_chg_handler(info); 590*27a28d32SJaewon Kim if (ret) 591*27a28d32SJaewon Kim dev_err(info->dev, "Unknown charger type\n"); 592*27a28d32SJaewon Kim info->irq_chg = false; 593*27a28d32SJaewon Kim } 594*27a28d32SJaewon Kim 595*27a28d32SJaewon Kim mutex_unlock(&info->mutex); 596*27a28d32SJaewon Kim } 597*27a28d32SJaewon Kim 598*27a28d32SJaewon Kim static irqreturn_t max77843_muic_irq_handler(int irq, void *data) 599*27a28d32SJaewon Kim { 600*27a28d32SJaewon Kim struct max77843_muic_info *info = data; 601*27a28d32SJaewon Kim int i, irq_type = -1; 602*27a28d32SJaewon Kim 603*27a28d32SJaewon Kim for (i = 0; i < ARRAY_SIZE(max77843_muic_irqs); i++) 604*27a28d32SJaewon Kim if (irq == max77843_muic_irqs[i].virq) 605*27a28d32SJaewon Kim irq_type = max77843_muic_irqs[i].irq; 606*27a28d32SJaewon Kim 607*27a28d32SJaewon Kim switch (irq_type) { 608*27a28d32SJaewon Kim case MAX77843_MUIC_IRQ_INT1_ADC: 609*27a28d32SJaewon Kim case MAX77843_MUIC_IRQ_INT1_ADCERROR: 610*27a28d32SJaewon Kim case MAX77843_MUIC_IRQ_INT1_ADC1K: 611*27a28d32SJaewon Kim info->irq_adc = true; 612*27a28d32SJaewon Kim break; 613*27a28d32SJaewon Kim case MAX77843_MUIC_IRQ_INT2_CHGTYP: 614*27a28d32SJaewon Kim case MAX77843_MUIC_IRQ_INT2_CHGDETRUN: 615*27a28d32SJaewon Kim case MAX77843_MUIC_IRQ_INT2_DCDTMR: 616*27a28d32SJaewon Kim case MAX77843_MUIC_IRQ_INT2_DXOVP: 617*27a28d32SJaewon Kim case MAX77843_MUIC_IRQ_INT2_VBVOLT: 618*27a28d32SJaewon Kim info->irq_chg = true; 619*27a28d32SJaewon Kim break; 620*27a28d32SJaewon Kim case MAX77843_MUIC_IRQ_INT3_VBADC: 621*27a28d32SJaewon Kim case MAX77843_MUIC_IRQ_INT3_VDNMON: 622*27a28d32SJaewon Kim case MAX77843_MUIC_IRQ_INT3_DNRES: 623*27a28d32SJaewon Kim case MAX77843_MUIC_IRQ_INT3_MPNACK: 624*27a28d32SJaewon Kim case MAX77843_MUIC_IRQ_INT3_MRXBUFOW: 625*27a28d32SJaewon Kim case MAX77843_MUIC_IRQ_INT3_MRXTRF: 626*27a28d32SJaewon Kim case MAX77843_MUIC_IRQ_INT3_MRXPERR: 627*27a28d32SJaewon Kim case MAX77843_MUIC_IRQ_INT3_MRXRDY: 628*27a28d32SJaewon Kim break; 629*27a28d32SJaewon Kim default: 630*27a28d32SJaewon Kim dev_err(info->dev, "Cannot recognize IRQ(%d)\n", irq_type); 631*27a28d32SJaewon Kim break; 632*27a28d32SJaewon Kim } 633*27a28d32SJaewon Kim 634*27a28d32SJaewon Kim schedule_work(&info->irq_work); 635*27a28d32SJaewon Kim 636*27a28d32SJaewon Kim return IRQ_HANDLED; 637*27a28d32SJaewon Kim } 638*27a28d32SJaewon Kim 639*27a28d32SJaewon Kim static void max77843_muic_detect_cable_wq(struct work_struct *work) 640*27a28d32SJaewon Kim { 641*27a28d32SJaewon Kim struct max77843_muic_info *info = container_of(to_delayed_work(work), 642*27a28d32SJaewon Kim struct max77843_muic_info, wq_detcable); 643*27a28d32SJaewon Kim struct max77843 *max77843 = info->max77843; 644*27a28d32SJaewon Kim int chg_type, adc, ret; 645*27a28d32SJaewon Kim bool attached; 646*27a28d32SJaewon Kim 647*27a28d32SJaewon Kim mutex_lock(&info->mutex); 648*27a28d32SJaewon Kim 649*27a28d32SJaewon Kim ret = regmap_bulk_read(max77843->regmap_muic, 650*27a28d32SJaewon Kim MAX77843_MUIC_REG_STATUS1, info->status, 651*27a28d32SJaewon Kim MAX77843_MUIC_STATUS_NUM); 652*27a28d32SJaewon Kim if (ret) { 653*27a28d32SJaewon Kim dev_err(info->dev, "Cannot read STATUS registers\n"); 654*27a28d32SJaewon Kim goto err_cable_wq; 655*27a28d32SJaewon Kim } 656*27a28d32SJaewon Kim 657*27a28d32SJaewon Kim adc = max77843_muic_get_cable_type(info, 658*27a28d32SJaewon Kim MAX77843_CABLE_GROUP_ADC, &attached); 659*27a28d32SJaewon Kim if (attached && adc != MAX77843_MUIC_ADC_OPEN) { 660*27a28d32SJaewon Kim ret = max77843_muic_adc_handler(info); 661*27a28d32SJaewon Kim if (ret < 0) { 662*27a28d32SJaewon Kim dev_err(info->dev, "Cannot detect accessory\n"); 663*27a28d32SJaewon Kim goto err_cable_wq; 664*27a28d32SJaewon Kim } 665*27a28d32SJaewon Kim } 666*27a28d32SJaewon Kim 667*27a28d32SJaewon Kim chg_type = max77843_muic_get_cable_type(info, 668*27a28d32SJaewon Kim MAX77843_CABLE_GROUP_CHG, &attached); 669*27a28d32SJaewon Kim if (attached && chg_type != MAX77843_MUIC_CHG_NONE) { 670*27a28d32SJaewon Kim ret = max77843_muic_chg_handler(info); 671*27a28d32SJaewon Kim if (ret < 0) { 672*27a28d32SJaewon Kim dev_err(info->dev, "Cannot detect charger accessory\n"); 673*27a28d32SJaewon Kim goto err_cable_wq; 674*27a28d32SJaewon Kim } 675*27a28d32SJaewon Kim } 676*27a28d32SJaewon Kim 677*27a28d32SJaewon Kim err_cable_wq: 678*27a28d32SJaewon Kim mutex_unlock(&info->mutex); 679*27a28d32SJaewon Kim } 680*27a28d32SJaewon Kim 681*27a28d32SJaewon Kim static int max77843_muic_set_debounce_time(struct max77843_muic_info *info, 682*27a28d32SJaewon Kim enum max77843_muic_adc_debounce_time time) 683*27a28d32SJaewon Kim { 684*27a28d32SJaewon Kim struct max77843 *max77843 = info->max77843; 685*27a28d32SJaewon Kim unsigned int ret; 686*27a28d32SJaewon Kim 687*27a28d32SJaewon Kim switch (time) { 688*27a28d32SJaewon Kim case MAX77843_DEBOUNCE_TIME_5MS: 689*27a28d32SJaewon Kim case MAX77843_DEBOUNCE_TIME_10MS: 690*27a28d32SJaewon Kim case MAX77843_DEBOUNCE_TIME_25MS: 691*27a28d32SJaewon Kim case MAX77843_DEBOUNCE_TIME_38_62MS: 692*27a28d32SJaewon Kim ret = regmap_update_bits(max77843->regmap_muic, 693*27a28d32SJaewon Kim MAX77843_MUIC_REG_CONTROL4, 694*27a28d32SJaewon Kim MAX77843_MUIC_CONTROL4_ADCDBSET_MASK, 695*27a28d32SJaewon Kim time << CONTROL4_ADCDBSET_SHIFT); 696*27a28d32SJaewon Kim if (ret < 0) { 697*27a28d32SJaewon Kim dev_err(info->dev, "Cannot write MUIC regmap\n"); 698*27a28d32SJaewon Kim return ret; 699*27a28d32SJaewon Kim } 700*27a28d32SJaewon Kim break; 701*27a28d32SJaewon Kim default: 702*27a28d32SJaewon Kim dev_err(info->dev, "Invalid ADC debounce time\n"); 703*27a28d32SJaewon Kim return -EINVAL; 704*27a28d32SJaewon Kim } 705*27a28d32SJaewon Kim 706*27a28d32SJaewon Kim return 0; 707*27a28d32SJaewon Kim } 708*27a28d32SJaewon Kim 709*27a28d32SJaewon Kim static int max77843_init_muic_regmap(struct max77843 *max77843) 710*27a28d32SJaewon Kim { 711*27a28d32SJaewon Kim int ret; 712*27a28d32SJaewon Kim 713*27a28d32SJaewon Kim max77843->i2c_muic = i2c_new_dummy(max77843->i2c->adapter, 714*27a28d32SJaewon Kim I2C_ADDR_MUIC); 715*27a28d32SJaewon Kim if (!max77843->i2c_muic) { 716*27a28d32SJaewon Kim dev_err(&max77843->i2c->dev, 717*27a28d32SJaewon Kim "Cannot allocate I2C device for MUIC\n"); 718*27a28d32SJaewon Kim return PTR_ERR(max77843->i2c_muic); 719*27a28d32SJaewon Kim } 720*27a28d32SJaewon Kim 721*27a28d32SJaewon Kim i2c_set_clientdata(max77843->i2c_muic, max77843); 722*27a28d32SJaewon Kim 723*27a28d32SJaewon Kim max77843->regmap_muic = devm_regmap_init_i2c(max77843->i2c_muic, 724*27a28d32SJaewon Kim &max77843_muic_regmap_config); 725*27a28d32SJaewon Kim if (IS_ERR(max77843->regmap_muic)) { 726*27a28d32SJaewon Kim ret = PTR_ERR(max77843->regmap_muic); 727*27a28d32SJaewon Kim goto err_muic_i2c; 728*27a28d32SJaewon Kim } 729*27a28d32SJaewon Kim 730*27a28d32SJaewon Kim ret = regmap_add_irq_chip(max77843->regmap_muic, max77843->irq, 731*27a28d32SJaewon Kim IRQF_TRIGGER_LOW | IRQF_ONESHOT | IRQF_SHARED, 732*27a28d32SJaewon Kim 0, &max77843_muic_irq_chip, &max77843->irq_data_muic); 733*27a28d32SJaewon Kim if (ret < 0) { 734*27a28d32SJaewon Kim dev_err(&max77843->i2c->dev, "Cannot add MUIC IRQ chip\n"); 735*27a28d32SJaewon Kim goto err_muic_i2c; 736*27a28d32SJaewon Kim } 737*27a28d32SJaewon Kim 738*27a28d32SJaewon Kim return 0; 739*27a28d32SJaewon Kim 740*27a28d32SJaewon Kim err_muic_i2c: 741*27a28d32SJaewon Kim i2c_unregister_device(max77843->i2c_muic); 742*27a28d32SJaewon Kim 743*27a28d32SJaewon Kim return ret; 744*27a28d32SJaewon Kim } 745*27a28d32SJaewon Kim 746*27a28d32SJaewon Kim static int max77843_muic_probe(struct platform_device *pdev) 747*27a28d32SJaewon Kim { 748*27a28d32SJaewon Kim struct max77843 *max77843 = dev_get_drvdata(pdev->dev.parent); 749*27a28d32SJaewon Kim struct max77843_muic_info *info; 750*27a28d32SJaewon Kim unsigned int id; 751*27a28d32SJaewon Kim int i, ret; 752*27a28d32SJaewon Kim 753*27a28d32SJaewon Kim info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); 754*27a28d32SJaewon Kim if (!info) 755*27a28d32SJaewon Kim return -ENOMEM; 756*27a28d32SJaewon Kim 757*27a28d32SJaewon Kim info->dev = &pdev->dev; 758*27a28d32SJaewon Kim info->max77843 = max77843; 759*27a28d32SJaewon Kim 760*27a28d32SJaewon Kim platform_set_drvdata(pdev, info); 761*27a28d32SJaewon Kim mutex_init(&info->mutex); 762*27a28d32SJaewon Kim 763*27a28d32SJaewon Kim /* Initialize i2c and regmap */ 764*27a28d32SJaewon Kim ret = max77843_init_muic_regmap(max77843); 765*27a28d32SJaewon Kim if (ret) { 766*27a28d32SJaewon Kim dev_err(&pdev->dev, "Failed to init MUIC regmap\n"); 767*27a28d32SJaewon Kim return ret; 768*27a28d32SJaewon Kim } 769*27a28d32SJaewon Kim 770*27a28d32SJaewon Kim /* Turn off auto detection configuration */ 771*27a28d32SJaewon Kim ret = regmap_update_bits(max77843->regmap_muic, 772*27a28d32SJaewon Kim MAX77843_MUIC_REG_CONTROL4, 773*27a28d32SJaewon Kim MAX77843_MUIC_CONTROL4_USBAUTO_MASK | 774*27a28d32SJaewon Kim MAX77843_MUIC_CONTROL4_FCTAUTO_MASK, 775*27a28d32SJaewon Kim CONTROL4_AUTO_DISABLE); 776*27a28d32SJaewon Kim 777*27a28d32SJaewon Kim /* Initialize extcon device */ 778*27a28d32SJaewon Kim info->edev = devm_extcon_dev_allocate(&pdev->dev, 779*27a28d32SJaewon Kim max77843_extcon_cable); 780*27a28d32SJaewon Kim if (IS_ERR(info->edev)) { 781*27a28d32SJaewon Kim dev_err(&pdev->dev, "Failed to allocate memory for extcon\n"); 782*27a28d32SJaewon Kim ret = -ENODEV; 783*27a28d32SJaewon Kim goto err_muic_irq; 784*27a28d32SJaewon Kim } 785*27a28d32SJaewon Kim 786*27a28d32SJaewon Kim ret = devm_extcon_dev_register(&pdev->dev, info->edev); 787*27a28d32SJaewon Kim if (ret) { 788*27a28d32SJaewon Kim dev_err(&pdev->dev, "Failed to register extcon device\n"); 789*27a28d32SJaewon Kim goto err_muic_irq; 790*27a28d32SJaewon Kim } 791*27a28d32SJaewon Kim 792*27a28d32SJaewon Kim /* Set ADC debounce time */ 793*27a28d32SJaewon Kim max77843_muic_set_debounce_time(info, MAX77843_DEBOUNCE_TIME_25MS); 794*27a28d32SJaewon Kim 795*27a28d32SJaewon Kim /* Set initial path for UART */ 796*27a28d32SJaewon Kim max77843_muic_set_path(info, CONTROL1_SW_UART, true); 797*27a28d32SJaewon Kim 798*27a28d32SJaewon Kim /* Check revision number of MUIC device */ 799*27a28d32SJaewon Kim ret = regmap_read(max77843->regmap_muic, MAX77843_MUIC_REG_ID, &id); 800*27a28d32SJaewon Kim if (ret < 0) { 801*27a28d32SJaewon Kim dev_err(&pdev->dev, "Failed to read revision number\n"); 802*27a28d32SJaewon Kim goto err_muic_irq; 803*27a28d32SJaewon Kim } 804*27a28d32SJaewon Kim dev_info(info->dev, "MUIC device ID : 0x%x\n", id); 805*27a28d32SJaewon Kim 806*27a28d32SJaewon Kim /* Support virtual irq domain for max77843 MUIC device */ 807*27a28d32SJaewon Kim INIT_WORK(&info->irq_work, max77843_muic_irq_work); 808*27a28d32SJaewon Kim 809*27a28d32SJaewon Kim for (i = 0; i < ARRAY_SIZE(max77843_muic_irqs); i++) { 810*27a28d32SJaewon Kim struct max77843_muic_irq *muic_irq = &max77843_muic_irqs[i]; 811*27a28d32SJaewon Kim unsigned int virq = 0; 812*27a28d32SJaewon Kim 813*27a28d32SJaewon Kim virq = regmap_irq_get_virq(max77843->irq_data_muic, 814*27a28d32SJaewon Kim muic_irq->irq); 815*27a28d32SJaewon Kim if (virq <= 0) { 816*27a28d32SJaewon Kim ret = -EINVAL; 817*27a28d32SJaewon Kim goto err_muic_irq; 818*27a28d32SJaewon Kim } 819*27a28d32SJaewon Kim muic_irq->virq = virq; 820*27a28d32SJaewon Kim 821*27a28d32SJaewon Kim ret = devm_request_threaded_irq(&pdev->dev, virq, NULL, 822*27a28d32SJaewon Kim max77843_muic_irq_handler, IRQF_NO_SUSPEND, 823*27a28d32SJaewon Kim muic_irq->name, info); 824*27a28d32SJaewon Kim if (ret) { 825*27a28d32SJaewon Kim dev_err(&pdev->dev, 826*27a28d32SJaewon Kim "Failed to request irq (IRQ: %d, error: %d)\n", 827*27a28d32SJaewon Kim muic_irq->irq, ret); 828*27a28d32SJaewon Kim goto err_muic_irq; 829*27a28d32SJaewon Kim } 830*27a28d32SJaewon Kim } 831*27a28d32SJaewon Kim 832*27a28d32SJaewon Kim /* Detect accessory after completing the initialization of platform */ 833*27a28d32SJaewon Kim INIT_DELAYED_WORK(&info->wq_detcable, max77843_muic_detect_cable_wq); 834*27a28d32SJaewon Kim queue_delayed_work(system_power_efficient_wq, 835*27a28d32SJaewon Kim &info->wq_detcable, msecs_to_jiffies(DELAY_MS_DEFAULT)); 836*27a28d32SJaewon Kim 837*27a28d32SJaewon Kim return 0; 838*27a28d32SJaewon Kim 839*27a28d32SJaewon Kim err_muic_irq: 840*27a28d32SJaewon Kim regmap_del_irq_chip(max77843->irq, max77843->irq_data_muic); 841*27a28d32SJaewon Kim i2c_unregister_device(max77843->i2c_muic); 842*27a28d32SJaewon Kim 843*27a28d32SJaewon Kim return ret; 844*27a28d32SJaewon Kim } 845*27a28d32SJaewon Kim 846*27a28d32SJaewon Kim static int max77843_muic_remove(struct platform_device *pdev) 847*27a28d32SJaewon Kim { 848*27a28d32SJaewon Kim struct max77843_muic_info *info = platform_get_drvdata(pdev); 849*27a28d32SJaewon Kim struct max77843 *max77843 = info->max77843; 850*27a28d32SJaewon Kim 851*27a28d32SJaewon Kim cancel_work_sync(&info->irq_work); 852*27a28d32SJaewon Kim regmap_del_irq_chip(max77843->irq, max77843->irq_data_muic); 853*27a28d32SJaewon Kim i2c_unregister_device(max77843->i2c_muic); 854*27a28d32SJaewon Kim 855*27a28d32SJaewon Kim return 0; 856*27a28d32SJaewon Kim } 857*27a28d32SJaewon Kim 858*27a28d32SJaewon Kim static const struct platform_device_id max77843_muic_id[] = { 859*27a28d32SJaewon Kim { "max77843-muic", }, 860*27a28d32SJaewon Kim { /* sentinel */ }, 861*27a28d32SJaewon Kim }; 862*27a28d32SJaewon Kim MODULE_DEVICE_TABLE(platform, max77843_muic_id); 863*27a28d32SJaewon Kim 864*27a28d32SJaewon Kim static struct platform_driver max77843_muic_driver = { 865*27a28d32SJaewon Kim .driver = { 866*27a28d32SJaewon Kim .name = "max77843-muic", 867*27a28d32SJaewon Kim }, 868*27a28d32SJaewon Kim .probe = max77843_muic_probe, 869*27a28d32SJaewon Kim .remove = max77843_muic_remove, 870*27a28d32SJaewon Kim .id_table = max77843_muic_id, 871*27a28d32SJaewon Kim }; 872*27a28d32SJaewon Kim 873*27a28d32SJaewon Kim static int __init max77843_muic_init(void) 874*27a28d32SJaewon Kim { 875*27a28d32SJaewon Kim return platform_driver_register(&max77843_muic_driver); 876*27a28d32SJaewon Kim } 877*27a28d32SJaewon Kim subsys_initcall(max77843_muic_init); 878*27a28d32SJaewon Kim 879*27a28d32SJaewon Kim MODULE_DESCRIPTION("Maxim MAX77843 Extcon driver"); 880*27a28d32SJaewon Kim MODULE_AUTHOR("Jaewon Kim <jaewon02.kim@samsung.com>"); 881*27a28d32SJaewon Kim MODULE_LICENSE("GPL"); 882