Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * extcon-sm5502.c - Silicon Mitus SM5502 extcon drvier to support USB switches
0004  *
0005  * Copyright (c) 2014 Samsung Electronics Co., Ltd
0006  * Author: Chanwoo Choi <cw00.choi@samsung.com>
0007  */
0008 
0009 #include <linux/err.h>
0010 #include <linux/i2c.h>
0011 #include <linux/interrupt.h>
0012 #include <linux/irqdomain.h>
0013 #include <linux/kernel.h>
0014 #include <linux/module.h>
0015 #include <linux/platform_device.h>
0016 #include <linux/regmap.h>
0017 #include <linux/slab.h>
0018 #include <linux/extcon-provider.h>
0019 
0020 #include "extcon-sm5502.h"
0021 
0022 #define DELAY_MS_DEFAULT        17000   /* unit: millisecond */
0023 
0024 struct muic_irq {
0025     unsigned int irq;
0026     const char *name;
0027     unsigned int virq;
0028 };
0029 
0030 struct reg_data {
0031     u8 reg;
0032     unsigned int val;
0033     bool invert;
0034 };
0035 
0036 struct sm5502_muic_info {
0037     struct device *dev;
0038     struct extcon_dev *edev;
0039 
0040     struct i2c_client *i2c;
0041     struct regmap *regmap;
0042 
0043     const struct sm5502_type *type;
0044     struct regmap_irq_chip_data *irq_data;
0045     int irq;
0046     bool irq_attach;
0047     bool irq_detach;
0048     struct work_struct irq_work;
0049 
0050     struct mutex mutex;
0051 
0052     /*
0053      * Use delayed workqueue to detect cable state and then
0054      * notify cable state to notifiee/platform through uevent.
0055      * After completing the booting of platform, the extcon provider
0056      * driver should notify cable state to upper layer.
0057      */
0058     struct delayed_work wq_detcable;
0059 };
0060 
0061 struct sm5502_type {
0062     struct muic_irq *muic_irqs;
0063     unsigned int num_muic_irqs;
0064     const struct regmap_irq_chip *irq_chip;
0065 
0066     struct reg_data *reg_data;
0067     unsigned int num_reg_data;
0068 
0069     unsigned int otg_dev_type1;
0070     int (*parse_irq)(struct sm5502_muic_info *info, int irq_type);
0071 };
0072 
0073 /* Default value of SM5502 register to bring up MUIC device. */
0074 static struct reg_data sm5502_reg_data[] = {
0075     {
0076         .reg = SM5502_REG_RESET,
0077         .val = SM5502_REG_RESET_MASK,
0078         .invert = true,
0079     }, {
0080         .reg = SM5502_REG_CONTROL,
0081         .val = SM5502_REG_CONTROL_MASK_INT_MASK,
0082         .invert = false,
0083     }, {
0084         .reg = SM5502_REG_INTMASK1,
0085         .val = SM5502_REG_INTM1_KP_MASK
0086             | SM5502_REG_INTM1_LKP_MASK
0087             | SM5502_REG_INTM1_LKR_MASK,
0088         .invert = true,
0089     }, {
0090         .reg = SM5502_REG_INTMASK2,
0091         .val = SM5502_REG_INTM2_VBUS_DET_MASK
0092             | SM5502_REG_INTM2_REV_ACCE_MASK
0093             | SM5502_REG_INTM2_ADC_CHG_MASK
0094             | SM5502_REG_INTM2_STUCK_KEY_MASK
0095             | SM5502_REG_INTM2_STUCK_KEY_RCV_MASK
0096             | SM5502_REG_INTM2_MHL_MASK,
0097         .invert = true,
0098     },
0099 };
0100 
0101 /* Default value of SM5504 register to bring up MUIC device. */
0102 static struct reg_data sm5504_reg_data[] = {
0103     {
0104         .reg = SM5502_REG_RESET,
0105         .val = SM5502_REG_RESET_MASK,
0106         .invert = true,
0107     }, {
0108         .reg = SM5502_REG_INTMASK1,
0109         .val = SM5504_REG_INTM1_ATTACH_MASK
0110             | SM5504_REG_INTM1_DETACH_MASK,
0111         .invert = false,
0112     }, {
0113         .reg = SM5502_REG_INTMASK2,
0114         .val = SM5504_REG_INTM2_RID_CHG_MASK
0115             | SM5504_REG_INTM2_UVLO_MASK
0116             | SM5504_REG_INTM2_POR_MASK,
0117         .invert = true,
0118     }, {
0119         .reg = SM5502_REG_CONTROL,
0120         .val = SM5502_REG_CONTROL_MANUAL_SW_MASK
0121             | SM5504_REG_CONTROL_CHGTYP_MASK
0122             | SM5504_REG_CONTROL_USBCHDEN_MASK
0123             | SM5504_REG_CONTROL_ADC_EN_MASK,
0124         .invert = true,
0125     },
0126 };
0127 
0128 /* List of detectable cables */
0129 static const unsigned int sm5502_extcon_cable[] = {
0130     EXTCON_USB,
0131     EXTCON_USB_HOST,
0132     EXTCON_CHG_USB_SDP,
0133     EXTCON_CHG_USB_DCP,
0134     EXTCON_NONE,
0135 };
0136 
0137 /* Define supported accessory type */
0138 enum sm5502_muic_acc_type {
0139     SM5502_MUIC_ADC_GROUND = 0x0,
0140     SM5502_MUIC_ADC_SEND_END_BUTTON,
0141     SM5502_MUIC_ADC_REMOTE_S1_BUTTON,
0142     SM5502_MUIC_ADC_REMOTE_S2_BUTTON,
0143     SM5502_MUIC_ADC_REMOTE_S3_BUTTON,
0144     SM5502_MUIC_ADC_REMOTE_S4_BUTTON,
0145     SM5502_MUIC_ADC_REMOTE_S5_BUTTON,
0146     SM5502_MUIC_ADC_REMOTE_S6_BUTTON,
0147     SM5502_MUIC_ADC_REMOTE_S7_BUTTON,
0148     SM5502_MUIC_ADC_REMOTE_S8_BUTTON,
0149     SM5502_MUIC_ADC_REMOTE_S9_BUTTON,
0150     SM5502_MUIC_ADC_REMOTE_S10_BUTTON,
0151     SM5502_MUIC_ADC_REMOTE_S11_BUTTON,
0152     SM5502_MUIC_ADC_REMOTE_S12_BUTTON,
0153     SM5502_MUIC_ADC_RESERVED_ACC_1,
0154     SM5502_MUIC_ADC_RESERVED_ACC_2,
0155     SM5502_MUIC_ADC_RESERVED_ACC_3,
0156     SM5502_MUIC_ADC_RESERVED_ACC_4,
0157     SM5502_MUIC_ADC_RESERVED_ACC_5,
0158     SM5502_MUIC_ADC_AUDIO_TYPE2,
0159     SM5502_MUIC_ADC_PHONE_POWERED_DEV,
0160     SM5502_MUIC_ADC_TTY_CONVERTER,
0161     SM5502_MUIC_ADC_UART_CABLE,
0162     SM5502_MUIC_ADC_TYPE1_CHARGER,
0163     SM5502_MUIC_ADC_FACTORY_MODE_BOOT_OFF_USB,
0164     SM5502_MUIC_ADC_FACTORY_MODE_BOOT_ON_USB,
0165     SM5502_MUIC_ADC_AUDIO_VIDEO_CABLE,
0166     SM5502_MUIC_ADC_TYPE2_CHARGER,
0167     SM5502_MUIC_ADC_FACTORY_MODE_BOOT_OFF_UART,
0168     SM5502_MUIC_ADC_FACTORY_MODE_BOOT_ON_UART,
0169     SM5502_MUIC_ADC_AUDIO_TYPE1,
0170     SM5502_MUIC_ADC_OPEN = 0x1f,
0171 
0172     /*
0173      * The below accessories have same ADC value (0x1f or 0x1e).
0174      * So, Device type1 is used to separate specific accessory.
0175      */
0176                             /* |---------|--ADC| */
0177                             /* |    [7:5]|[4:0]| */
0178     SM5502_MUIC_ADC_AUDIO_TYPE1_FULL_REMOTE = 0x3e, /* |      001|11110| */
0179     SM5502_MUIC_ADC_AUDIO_TYPE1_SEND_END = 0x5e,    /* |      010|11110| */
0180                             /* |Dev Type1|--ADC| */
0181     SM5502_MUIC_ADC_GROUND_USB_OTG = 0x80,      /* |      100|00000| */
0182     SM5502_MUIC_ADC_OPEN_USB = 0x5f,        /* |      010|11111| */
0183     SM5502_MUIC_ADC_OPEN_TA = 0xdf,         /* |      110|11111| */
0184     SM5502_MUIC_ADC_OPEN_USB_OTG = 0xff,        /* |      111|11111| */
0185 };
0186 
0187 /* List of supported interrupt for SM5502 */
0188 static struct muic_irq sm5502_muic_irqs[] = {
0189     { SM5502_IRQ_INT1_ATTACH,   "muic-attach" },
0190     { SM5502_IRQ_INT1_DETACH,   "muic-detach" },
0191     { SM5502_IRQ_INT1_KP,       "muic-kp" },
0192     { SM5502_IRQ_INT1_LKP,      "muic-lkp" },
0193     { SM5502_IRQ_INT1_LKR,      "muic-lkr" },
0194     { SM5502_IRQ_INT1_OVP_EVENT,    "muic-ovp-event" },
0195     { SM5502_IRQ_INT1_OCP_EVENT,    "muic-ocp-event" },
0196     { SM5502_IRQ_INT1_OVP_OCP_DIS,  "muic-ovp-ocp-dis" },
0197     { SM5502_IRQ_INT2_VBUS_DET, "muic-vbus-det" },
0198     { SM5502_IRQ_INT2_REV_ACCE, "muic-rev-acce" },
0199     { SM5502_IRQ_INT2_ADC_CHG,  "muic-adc-chg" },
0200     { SM5502_IRQ_INT2_STUCK_KEY,    "muic-stuck-key" },
0201     { SM5502_IRQ_INT2_STUCK_KEY_RCV, "muic-stuck-key-rcv" },
0202     { SM5502_IRQ_INT2_MHL,      "muic-mhl" },
0203 };
0204 
0205 /* Define interrupt list of SM5502 to register regmap_irq */
0206 static const struct regmap_irq sm5502_irqs[] = {
0207     /* INT1 interrupts */
0208     { .reg_offset = 0, .mask = SM5502_IRQ_INT1_ATTACH_MASK, },
0209     { .reg_offset = 0, .mask = SM5502_IRQ_INT1_DETACH_MASK, },
0210     { .reg_offset = 0, .mask = SM5502_IRQ_INT1_KP_MASK, },
0211     { .reg_offset = 0, .mask = SM5502_IRQ_INT1_LKP_MASK, },
0212     { .reg_offset = 0, .mask = SM5502_IRQ_INT1_LKR_MASK, },
0213     { .reg_offset = 0, .mask = SM5502_IRQ_INT1_OVP_EVENT_MASK, },
0214     { .reg_offset = 0, .mask = SM5502_IRQ_INT1_OCP_EVENT_MASK, },
0215     { .reg_offset = 0, .mask = SM5502_IRQ_INT1_OVP_OCP_DIS_MASK, },
0216 
0217     /* INT2 interrupts */
0218     { .reg_offset = 1, .mask = SM5502_IRQ_INT2_VBUS_DET_MASK,},
0219     { .reg_offset = 1, .mask = SM5502_IRQ_INT2_REV_ACCE_MASK, },
0220     { .reg_offset = 1, .mask = SM5502_IRQ_INT2_ADC_CHG_MASK, },
0221     { .reg_offset = 1, .mask = SM5502_IRQ_INT2_STUCK_KEY_MASK, },
0222     { .reg_offset = 1, .mask = SM5502_IRQ_INT2_STUCK_KEY_RCV_MASK, },
0223     { .reg_offset = 1, .mask = SM5502_IRQ_INT2_MHL_MASK, },
0224 };
0225 
0226 static const struct regmap_irq_chip sm5502_muic_irq_chip = {
0227     .name           = "sm5502",
0228     .status_base        = SM5502_REG_INT1,
0229     .mask_base      = SM5502_REG_INTMASK1,
0230     .num_regs       = 2,
0231     .irqs           = sm5502_irqs,
0232     .num_irqs       = ARRAY_SIZE(sm5502_irqs),
0233 };
0234 
0235 /* List of supported interrupt for SM5504 */
0236 static struct muic_irq sm5504_muic_irqs[] = {
0237     { SM5504_IRQ_INT1_ATTACH,   "muic-attach" },
0238     { SM5504_IRQ_INT1_DETACH,   "muic-detach" },
0239     { SM5504_IRQ_INT1_CHG_DET,  "muic-chg-det" },
0240     { SM5504_IRQ_INT1_DCD_OUT,  "muic-dcd-out" },
0241     { SM5504_IRQ_INT1_OVP_EVENT,    "muic-ovp-event" },
0242     { SM5504_IRQ_INT1_CONNECT,  "muic-connect" },
0243     { SM5504_IRQ_INT1_ADC_CHG,  "muic-adc-chg" },
0244     { SM5504_IRQ_INT2_RID_CHG,  "muic-rid-chg" },
0245     { SM5504_IRQ_INT2_UVLO,     "muic-uvlo" },
0246     { SM5504_IRQ_INT2_POR,      "muic-por" },
0247     { SM5504_IRQ_INT2_OVP_FET,  "muic-ovp-fet" },
0248     { SM5504_IRQ_INT2_OCP_LATCH,    "muic-ocp-latch" },
0249     { SM5504_IRQ_INT2_OCP_EVENT,    "muic-ocp-event" },
0250     { SM5504_IRQ_INT2_OVP_OCP_EVENT, "muic-ovp-ocp-event" },
0251 };
0252 
0253 /* Define interrupt list of SM5504 to register regmap_irq */
0254 static const struct regmap_irq sm5504_irqs[] = {
0255     /* INT1 interrupts */
0256     { .reg_offset = 0, .mask = SM5504_IRQ_INT1_ATTACH_MASK, },
0257     { .reg_offset = 0, .mask = SM5504_IRQ_INT1_DETACH_MASK, },
0258     { .reg_offset = 0, .mask = SM5504_IRQ_INT1_CHG_DET_MASK, },
0259     { .reg_offset = 0, .mask = SM5504_IRQ_INT1_DCD_OUT_MASK, },
0260     { .reg_offset = 0, .mask = SM5504_IRQ_INT1_OVP_MASK, },
0261     { .reg_offset = 0, .mask = SM5504_IRQ_INT1_CONNECT_MASK, },
0262     { .reg_offset = 0, .mask = SM5504_IRQ_INT1_ADC_CHG_MASK, },
0263 
0264     /* INT2 interrupts */
0265     { .reg_offset = 1, .mask = SM5504_IRQ_INT2_RID_CHG_MASK,},
0266     { .reg_offset = 1, .mask = SM5504_IRQ_INT2_UVLO_MASK, },
0267     { .reg_offset = 1, .mask = SM5504_IRQ_INT2_POR_MASK, },
0268     { .reg_offset = 1, .mask = SM5504_IRQ_INT2_OVP_FET_MASK, },
0269     { .reg_offset = 1, .mask = SM5504_IRQ_INT2_OCP_LATCH_MASK, },
0270     { .reg_offset = 1, .mask = SM5504_IRQ_INT2_OCP_EVENT_MASK, },
0271     { .reg_offset = 1, .mask = SM5504_IRQ_INT2_OVP_OCP_EVENT_MASK, },
0272 };
0273 
0274 static const struct regmap_irq_chip sm5504_muic_irq_chip = {
0275     .name           = "sm5504",
0276     .status_base        = SM5502_REG_INT1,
0277     .mask_base      = SM5502_REG_INTMASK1,
0278     .num_regs       = 2,
0279     .irqs           = sm5504_irqs,
0280     .num_irqs       = ARRAY_SIZE(sm5504_irqs),
0281 };
0282 
0283 /* Define regmap configuration of SM5502 for I2C communication  */
0284 static bool sm5502_muic_volatile_reg(struct device *dev, unsigned int reg)
0285 {
0286     switch (reg) {
0287     case SM5502_REG_INTMASK1:
0288     case SM5502_REG_INTMASK2:
0289         return true;
0290     default:
0291         break;
0292     }
0293     return false;
0294 }
0295 
0296 static const struct regmap_config sm5502_muic_regmap_config = {
0297     .reg_bits   = 8,
0298     .val_bits   = 8,
0299     .volatile_reg   = sm5502_muic_volatile_reg,
0300     .max_register   = SM5502_REG_END,
0301 };
0302 
0303 /* Change DM_CON/DP_CON/VBUSIN switch according to cable type */
0304 static int sm5502_muic_set_path(struct sm5502_muic_info *info,
0305                 unsigned int con_sw, unsigned int vbus_sw,
0306                 bool attached)
0307 {
0308     int ret;
0309 
0310     if (!attached) {
0311         con_sw  = DM_DP_SWITCH_OPEN;
0312         vbus_sw = VBUSIN_SWITCH_OPEN;
0313     }
0314 
0315     switch (con_sw) {
0316     case DM_DP_SWITCH_OPEN:
0317     case DM_DP_SWITCH_USB:
0318     case DM_DP_SWITCH_AUDIO:
0319     case DM_DP_SWITCH_UART:
0320         ret = regmap_update_bits(info->regmap, SM5502_REG_MANUAL_SW1,
0321                      SM5502_REG_MANUAL_SW1_DP_MASK |
0322                      SM5502_REG_MANUAL_SW1_DM_MASK,
0323                      con_sw);
0324         if (ret < 0) {
0325             dev_err(info->dev,
0326                 "cannot update DM_CON/DP_CON switch\n");
0327             return ret;
0328         }
0329         break;
0330     default:
0331         dev_err(info->dev, "Unknown DM_CON/DP_CON switch type (%d)\n",
0332                 con_sw);
0333         return -EINVAL;
0334     }
0335 
0336     switch (vbus_sw) {
0337     case VBUSIN_SWITCH_OPEN:
0338     case VBUSIN_SWITCH_VBUSOUT:
0339     case VBUSIN_SWITCH_MIC:
0340     case VBUSIN_SWITCH_VBUSOUT_WITH_USB:
0341         ret = regmap_update_bits(info->regmap, SM5502_REG_MANUAL_SW1,
0342                      SM5502_REG_MANUAL_SW1_VBUSIN_MASK,
0343                      vbus_sw);
0344         if (ret < 0) {
0345             dev_err(info->dev,
0346                 "cannot update VBUSIN switch\n");
0347             return ret;
0348         }
0349         break;
0350     default:
0351         dev_err(info->dev, "Unknown VBUS switch type (%d)\n", vbus_sw);
0352         return -EINVAL;
0353     }
0354 
0355     return 0;
0356 }
0357 
0358 /* Return cable type of attached or detached accessories */
0359 static unsigned int sm5502_muic_get_cable_type(struct sm5502_muic_info *info)
0360 {
0361     unsigned int cable_type, adc, dev_type1;
0362     int ret;
0363 
0364     /* Read ADC value according to external cable or button */
0365     ret = regmap_read(info->regmap, SM5502_REG_ADC, &adc);
0366     if (ret) {
0367         dev_err(info->dev, "failed to read ADC register\n");
0368         return ret;
0369     }
0370 
0371     /*
0372      * If ADC is SM5502_MUIC_ADC_GROUND(0x0), external cable hasn't
0373      * connected with to MUIC device.
0374      */
0375     cable_type = adc & SM5502_REG_ADC_MASK;
0376 
0377     switch (cable_type) {
0378     case SM5502_MUIC_ADC_GROUND:
0379         ret = regmap_read(info->regmap, SM5502_REG_DEV_TYPE1,
0380                   &dev_type1);
0381         if (ret) {
0382             dev_err(info->dev, "failed to read DEV_TYPE1 reg\n");
0383             return ret;
0384         }
0385 
0386         if (dev_type1 == info->type->otg_dev_type1) {
0387             cable_type = SM5502_MUIC_ADC_GROUND_USB_OTG;
0388         } else {
0389             dev_dbg(info->dev,
0390                 "cannot identify the cable type: adc(0x%x), dev_type1(0x%x)\n",
0391                 adc, dev_type1);
0392             return -EINVAL;
0393         }
0394         break;
0395     case SM5502_MUIC_ADC_SEND_END_BUTTON:
0396     case SM5502_MUIC_ADC_REMOTE_S1_BUTTON:
0397     case SM5502_MUIC_ADC_REMOTE_S2_BUTTON:
0398     case SM5502_MUIC_ADC_REMOTE_S3_BUTTON:
0399     case SM5502_MUIC_ADC_REMOTE_S4_BUTTON:
0400     case SM5502_MUIC_ADC_REMOTE_S5_BUTTON:
0401     case SM5502_MUIC_ADC_REMOTE_S6_BUTTON:
0402     case SM5502_MUIC_ADC_REMOTE_S7_BUTTON:
0403     case SM5502_MUIC_ADC_REMOTE_S8_BUTTON:
0404     case SM5502_MUIC_ADC_REMOTE_S9_BUTTON:
0405     case SM5502_MUIC_ADC_REMOTE_S10_BUTTON:
0406     case SM5502_MUIC_ADC_REMOTE_S11_BUTTON:
0407     case SM5502_MUIC_ADC_REMOTE_S12_BUTTON:
0408     case SM5502_MUIC_ADC_RESERVED_ACC_1:
0409     case SM5502_MUIC_ADC_RESERVED_ACC_2:
0410     case SM5502_MUIC_ADC_RESERVED_ACC_3:
0411     case SM5502_MUIC_ADC_RESERVED_ACC_4:
0412     case SM5502_MUIC_ADC_RESERVED_ACC_5:
0413     case SM5502_MUIC_ADC_AUDIO_TYPE2:
0414     case SM5502_MUIC_ADC_PHONE_POWERED_DEV:
0415     case SM5502_MUIC_ADC_TTY_CONVERTER:
0416     case SM5502_MUIC_ADC_UART_CABLE:
0417     case SM5502_MUIC_ADC_TYPE1_CHARGER:
0418     case SM5502_MUIC_ADC_FACTORY_MODE_BOOT_OFF_USB:
0419     case SM5502_MUIC_ADC_FACTORY_MODE_BOOT_ON_USB:
0420     case SM5502_MUIC_ADC_AUDIO_VIDEO_CABLE:
0421     case SM5502_MUIC_ADC_TYPE2_CHARGER:
0422     case SM5502_MUIC_ADC_FACTORY_MODE_BOOT_OFF_UART:
0423     case SM5502_MUIC_ADC_FACTORY_MODE_BOOT_ON_UART:
0424         break;
0425     case SM5502_MUIC_ADC_AUDIO_TYPE1:
0426         /*
0427          * Check whether cable type is
0428          * SM5502_MUIC_ADC_AUDIO_TYPE1_FULL_REMOTE
0429          * or SM5502_MUIC_ADC_AUDIO_TYPE1_SEND_END
0430          * by using Button event.
0431          */
0432         break;
0433     case SM5502_MUIC_ADC_OPEN:
0434         ret = regmap_read(info->regmap, SM5502_REG_DEV_TYPE1,
0435                   &dev_type1);
0436         if (ret) {
0437             dev_err(info->dev, "failed to read DEV_TYPE1 reg\n");
0438             return ret;
0439         }
0440 
0441         if (dev_type1 == info->type->otg_dev_type1) {
0442             cable_type = SM5502_MUIC_ADC_OPEN_USB_OTG;
0443             break;
0444         }
0445 
0446         switch (dev_type1) {
0447         case SM5502_REG_DEV_TYPE1_USB_SDP_MASK:
0448             cable_type = SM5502_MUIC_ADC_OPEN_USB;
0449             break;
0450         case SM5502_REG_DEV_TYPE1_DEDICATED_CHG_MASK:
0451             cable_type = SM5502_MUIC_ADC_OPEN_TA;
0452             break;
0453         default:
0454             dev_dbg(info->dev,
0455                 "cannot identify the cable type: adc(0x%x)\n",
0456                 adc);
0457             return -EINVAL;
0458         }
0459         break;
0460     default:
0461         dev_err(info->dev,
0462             "failed to identify the cable type: adc(0x%x)\n", adc);
0463         return -EINVAL;
0464     }
0465 
0466     return cable_type;
0467 }
0468 
0469 static int sm5502_muic_cable_handler(struct sm5502_muic_info *info,
0470                      bool attached)
0471 {
0472     static unsigned int prev_cable_type = SM5502_MUIC_ADC_GROUND;
0473     unsigned int cable_type = SM5502_MUIC_ADC_GROUND;
0474     unsigned int con_sw = DM_DP_SWITCH_OPEN;
0475     unsigned int vbus_sw = VBUSIN_SWITCH_OPEN;
0476     unsigned int id;
0477     int ret;
0478 
0479     /* Get the type of attached or detached cable */
0480     if (attached)
0481         cable_type = sm5502_muic_get_cable_type(info);
0482     else
0483         cable_type = prev_cable_type;
0484     prev_cable_type = cable_type;
0485 
0486     switch (cable_type) {
0487     case SM5502_MUIC_ADC_OPEN_USB:
0488         id  = EXTCON_USB;
0489         con_sw  = DM_DP_SWITCH_USB;
0490         vbus_sw = VBUSIN_SWITCH_VBUSOUT_WITH_USB;
0491         break;
0492     case SM5502_MUIC_ADC_OPEN_TA:
0493         id  = EXTCON_CHG_USB_DCP;
0494         con_sw  = DM_DP_SWITCH_OPEN;
0495         vbus_sw = VBUSIN_SWITCH_VBUSOUT;
0496         break;
0497     case SM5502_MUIC_ADC_GROUND_USB_OTG:
0498     case SM5502_MUIC_ADC_OPEN_USB_OTG:
0499         id  = EXTCON_USB_HOST;
0500         con_sw  = DM_DP_SWITCH_USB;
0501         vbus_sw = VBUSIN_SWITCH_OPEN;
0502         break;
0503     default:
0504         dev_dbg(info->dev,
0505             "cannot handle this cable_type (0x%x)\n", cable_type);
0506         return 0;
0507     }
0508 
0509     /* Change internal hardware path(DM_CON/DP_CON, VBUSIN) */
0510     ret = sm5502_muic_set_path(info, con_sw, vbus_sw, attached);
0511     if (ret < 0)
0512         return ret;
0513 
0514     /* Change the state of external accessory */
0515     extcon_set_state_sync(info->edev, id, attached);
0516     if (id == EXTCON_USB)
0517         extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SDP,
0518                     attached);
0519 
0520     return 0;
0521 }
0522 
0523 static void sm5502_muic_irq_work(struct work_struct *work)
0524 {
0525     struct sm5502_muic_info *info = container_of(work,
0526             struct sm5502_muic_info, irq_work);
0527     int ret = 0;
0528 
0529     if (!info->edev)
0530         return;
0531 
0532     mutex_lock(&info->mutex);
0533 
0534     /* Detect attached or detached cables */
0535     if (info->irq_attach) {
0536         ret = sm5502_muic_cable_handler(info, true);
0537         info->irq_attach = false;
0538     }
0539     if (info->irq_detach) {
0540         ret = sm5502_muic_cable_handler(info, false);
0541         info->irq_detach = false;
0542     }
0543 
0544     if (ret < 0)
0545         dev_err(info->dev, "failed to handle MUIC interrupt\n");
0546 
0547     mutex_unlock(&info->mutex);
0548 }
0549 
0550 /*
0551  * Sets irq_attach or irq_detach in sm5502_muic_info and returns 0.
0552  * Returns -ESRCH if irq_type does not match registered IRQ for this dev type.
0553  */
0554 static int sm5502_parse_irq(struct sm5502_muic_info *info, int irq_type)
0555 {
0556     switch (irq_type) {
0557     case SM5502_IRQ_INT1_ATTACH:
0558         info->irq_attach = true;
0559         break;
0560     case SM5502_IRQ_INT1_DETACH:
0561         info->irq_detach = true;
0562         break;
0563     case SM5502_IRQ_INT1_KP:
0564     case SM5502_IRQ_INT1_LKP:
0565     case SM5502_IRQ_INT1_LKR:
0566     case SM5502_IRQ_INT1_OVP_EVENT:
0567     case SM5502_IRQ_INT1_OCP_EVENT:
0568     case SM5502_IRQ_INT1_OVP_OCP_DIS:
0569     case SM5502_IRQ_INT2_VBUS_DET:
0570     case SM5502_IRQ_INT2_REV_ACCE:
0571     case SM5502_IRQ_INT2_ADC_CHG:
0572     case SM5502_IRQ_INT2_STUCK_KEY:
0573     case SM5502_IRQ_INT2_STUCK_KEY_RCV:
0574     case SM5502_IRQ_INT2_MHL:
0575     default:
0576         break;
0577     }
0578 
0579     return 0;
0580 }
0581 
0582 static int sm5504_parse_irq(struct sm5502_muic_info *info, int irq_type)
0583 {
0584     switch (irq_type) {
0585     case SM5504_IRQ_INT1_ATTACH:
0586         info->irq_attach = true;
0587         break;
0588     case SM5504_IRQ_INT1_DETACH:
0589         info->irq_detach = true;
0590         break;
0591     case SM5504_IRQ_INT1_CHG_DET:
0592     case SM5504_IRQ_INT1_DCD_OUT:
0593     case SM5504_IRQ_INT1_OVP_EVENT:
0594     case SM5504_IRQ_INT1_CONNECT:
0595     case SM5504_IRQ_INT1_ADC_CHG:
0596     case SM5504_IRQ_INT2_RID_CHG:
0597     case SM5504_IRQ_INT2_UVLO:
0598     case SM5504_IRQ_INT2_POR:
0599     case SM5504_IRQ_INT2_OVP_FET:
0600     case SM5504_IRQ_INT2_OCP_LATCH:
0601     case SM5504_IRQ_INT2_OCP_EVENT:
0602     case SM5504_IRQ_INT2_OVP_OCP_EVENT:
0603     default:
0604         break;
0605     }
0606 
0607     return 0;
0608 }
0609 
0610 static irqreturn_t sm5502_muic_irq_handler(int irq, void *data)
0611 {
0612     struct sm5502_muic_info *info = data;
0613     int i, irq_type = -1, ret;
0614 
0615     for (i = 0; i < info->type->num_muic_irqs; i++)
0616         if (irq == info->type->muic_irqs[i].virq)
0617             irq_type = info->type->muic_irqs[i].irq;
0618 
0619     ret = info->type->parse_irq(info, irq_type);
0620     if (ret < 0) {
0621         dev_warn(info->dev, "cannot handle is interrupt:%d\n",
0622                     irq_type);
0623         return IRQ_HANDLED;
0624     }
0625     schedule_work(&info->irq_work);
0626 
0627     return IRQ_HANDLED;
0628 }
0629 
0630 static void sm5502_muic_detect_cable_wq(struct work_struct *work)
0631 {
0632     struct sm5502_muic_info *info = container_of(to_delayed_work(work),
0633                 struct sm5502_muic_info, wq_detcable);
0634     int ret;
0635 
0636     /* Notify the state of connector cable or not  */
0637     ret = sm5502_muic_cable_handler(info, true);
0638     if (ret < 0)
0639         dev_warn(info->dev, "failed to detect cable state\n");
0640 }
0641 
0642 static void sm5502_init_dev_type(struct sm5502_muic_info *info)
0643 {
0644     unsigned int reg_data, vendor_id, version_id;
0645     int i, ret;
0646 
0647     /* To test I2C, Print version_id and vendor_id of SM5502 */
0648     ret = regmap_read(info->regmap, SM5502_REG_DEVICE_ID, &reg_data);
0649     if (ret) {
0650         dev_err(info->dev,
0651             "failed to read DEVICE_ID register: %d\n", ret);
0652         return;
0653     }
0654 
0655     vendor_id = ((reg_data & SM5502_REG_DEVICE_ID_VENDOR_MASK) >>
0656                 SM5502_REG_DEVICE_ID_VENDOR_SHIFT);
0657     version_id = ((reg_data & SM5502_REG_DEVICE_ID_VERSION_MASK) >>
0658                 SM5502_REG_DEVICE_ID_VERSION_SHIFT);
0659 
0660     dev_info(info->dev, "Device type: version: 0x%x, vendor: 0x%x\n",
0661                 version_id, vendor_id);
0662 
0663     /* Initiazle the register of SM5502 device to bring-up */
0664     for (i = 0; i < info->type->num_reg_data; i++) {
0665         unsigned int val = 0;
0666 
0667         if (!info->type->reg_data[i].invert)
0668             val |= ~info->type->reg_data[i].val;
0669         else
0670             val = info->type->reg_data[i].val;
0671         regmap_write(info->regmap, info->type->reg_data[i].reg, val);
0672     }
0673 }
0674 
0675 static int sm5022_muic_i2c_probe(struct i2c_client *i2c)
0676 {
0677     struct device_node *np = i2c->dev.of_node;
0678     struct sm5502_muic_info *info;
0679     int i, ret, irq_flags;
0680 
0681     if (!np)
0682         return -EINVAL;
0683 
0684     info = devm_kzalloc(&i2c->dev, sizeof(*info), GFP_KERNEL);
0685     if (!info)
0686         return -ENOMEM;
0687     i2c_set_clientdata(i2c, info);
0688 
0689     info->dev = &i2c->dev;
0690     info->i2c = i2c;
0691     info->irq = i2c->irq;
0692     info->type = device_get_match_data(info->dev);
0693     if (!info->type)
0694         return -EINVAL;
0695     if (!info->type->parse_irq) {
0696         dev_err(info->dev, "parse_irq missing in struct sm5502_type\n");
0697         return -EINVAL;
0698     }
0699 
0700     mutex_init(&info->mutex);
0701 
0702     INIT_WORK(&info->irq_work, sm5502_muic_irq_work);
0703 
0704     info->regmap = devm_regmap_init_i2c(i2c, &sm5502_muic_regmap_config);
0705     if (IS_ERR(info->regmap)) {
0706         ret = PTR_ERR(info->regmap);
0707         dev_err(info->dev, "failed to allocate register map: %d\n",
0708                    ret);
0709         return ret;
0710     }
0711 
0712     /* Support irq domain for SM5502 MUIC device */
0713     irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT | IRQF_SHARED;
0714     ret = devm_regmap_add_irq_chip(info->dev, info->regmap, info->irq,
0715                        irq_flags, 0, info->type->irq_chip,
0716                        &info->irq_data);
0717     if (ret != 0) {
0718         dev_err(info->dev, "failed to request IRQ %d: %d\n",
0719                     info->irq, ret);
0720         return ret;
0721     }
0722 
0723     for (i = 0; i < info->type->num_muic_irqs; i++) {
0724         struct muic_irq *muic_irq = &info->type->muic_irqs[i];
0725         int virq = 0;
0726 
0727         virq = regmap_irq_get_virq(info->irq_data, muic_irq->irq);
0728         if (virq <= 0)
0729             return -EINVAL;
0730         muic_irq->virq = virq;
0731 
0732         ret = devm_request_threaded_irq(info->dev, virq, NULL,
0733                         sm5502_muic_irq_handler,
0734                         IRQF_NO_SUSPEND | IRQF_ONESHOT,
0735                         muic_irq->name, info);
0736         if (ret) {
0737             dev_err(info->dev,
0738                 "failed: irq request (IRQ: %d, error :%d)\n",
0739                 muic_irq->irq, ret);
0740             return ret;
0741         }
0742     }
0743 
0744     /* Allocate extcon device */
0745     info->edev = devm_extcon_dev_allocate(info->dev, sm5502_extcon_cable);
0746     if (IS_ERR(info->edev)) {
0747         dev_err(info->dev, "failed to allocate memory for extcon\n");
0748         return -ENOMEM;
0749     }
0750 
0751     /* Register extcon device */
0752     ret = devm_extcon_dev_register(info->dev, info->edev);
0753     if (ret) {
0754         dev_err(info->dev, "failed to register extcon device\n");
0755         return ret;
0756     }
0757 
0758     /*
0759      * Detect accessory after completing the initialization of platform
0760      *
0761      * - Use delayed workqueue to detect cable state and then
0762      * notify cable state to notifiee/platform through uevent.
0763      * After completing the booting of platform, the extcon provider
0764      * driver should notify cable state to upper layer.
0765      */
0766     INIT_DELAYED_WORK(&info->wq_detcable, sm5502_muic_detect_cable_wq);
0767     queue_delayed_work(system_power_efficient_wq, &info->wq_detcable,
0768             msecs_to_jiffies(DELAY_MS_DEFAULT));
0769 
0770     /* Initialize SM5502 device and print vendor id and version id */
0771     sm5502_init_dev_type(info);
0772 
0773     return 0;
0774 }
0775 
0776 static const struct sm5502_type sm5502_data = {
0777     .muic_irqs = sm5502_muic_irqs,
0778     .num_muic_irqs = ARRAY_SIZE(sm5502_muic_irqs),
0779     .irq_chip = &sm5502_muic_irq_chip,
0780     .reg_data = sm5502_reg_data,
0781     .num_reg_data = ARRAY_SIZE(sm5502_reg_data),
0782     .otg_dev_type1 = SM5502_REG_DEV_TYPE1_USB_OTG_MASK,
0783     .parse_irq = sm5502_parse_irq,
0784 };
0785 
0786 static const struct sm5502_type sm5504_data = {
0787     .muic_irqs = sm5504_muic_irqs,
0788     .num_muic_irqs = ARRAY_SIZE(sm5504_muic_irqs),
0789     .irq_chip = &sm5504_muic_irq_chip,
0790     .reg_data = sm5504_reg_data,
0791     .num_reg_data = ARRAY_SIZE(sm5504_reg_data),
0792     .otg_dev_type1 = SM5504_REG_DEV_TYPE1_USB_OTG_MASK,
0793     .parse_irq = sm5504_parse_irq,
0794 };
0795 
0796 static const struct of_device_id sm5502_dt_match[] = {
0797     { .compatible = "siliconmitus,sm5502-muic", .data = &sm5502_data },
0798     { .compatible = "siliconmitus,sm5504-muic", .data = &sm5504_data },
0799     { .compatible = "siliconmitus,sm5703-muic", .data = &sm5502_data },
0800     { },
0801 };
0802 MODULE_DEVICE_TABLE(of, sm5502_dt_match);
0803 
0804 #ifdef CONFIG_PM_SLEEP
0805 static int sm5502_muic_suspend(struct device *dev)
0806 {
0807     struct i2c_client *i2c = to_i2c_client(dev);
0808     struct sm5502_muic_info *info = i2c_get_clientdata(i2c);
0809 
0810     enable_irq_wake(info->irq);
0811 
0812     return 0;
0813 }
0814 
0815 static int sm5502_muic_resume(struct device *dev)
0816 {
0817     struct i2c_client *i2c = to_i2c_client(dev);
0818     struct sm5502_muic_info *info = i2c_get_clientdata(i2c);
0819 
0820     disable_irq_wake(info->irq);
0821 
0822     return 0;
0823 }
0824 #endif
0825 
0826 static SIMPLE_DEV_PM_OPS(sm5502_muic_pm_ops,
0827              sm5502_muic_suspend, sm5502_muic_resume);
0828 
0829 static const struct i2c_device_id sm5502_i2c_id[] = {
0830     { "sm5502", (kernel_ulong_t)&sm5502_data },
0831     { "sm5504", (kernel_ulong_t)&sm5504_data },
0832     { "sm5703-muic", (kernel_ulong_t)&sm5502_data },
0833     { }
0834 };
0835 MODULE_DEVICE_TABLE(i2c, sm5502_i2c_id);
0836 
0837 static struct i2c_driver sm5502_muic_i2c_driver = {
0838     .driver     = {
0839         .name   = "sm5502",
0840         .pm = &sm5502_muic_pm_ops,
0841         .of_match_table = sm5502_dt_match,
0842     },
0843     .probe_new = sm5022_muic_i2c_probe,
0844     .id_table = sm5502_i2c_id,
0845 };
0846 
0847 static int __init sm5502_muic_i2c_init(void)
0848 {
0849     return i2c_add_driver(&sm5502_muic_i2c_driver);
0850 }
0851 subsys_initcall(sm5502_muic_i2c_init);
0852 
0853 MODULE_DESCRIPTION("Silicon Mitus SM5502 Extcon driver");
0854 MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>");
0855 MODULE_LICENSE("GPL");