12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 2914b881fSChanwoo Choi /* 3914b881fSChanwoo Choi * extcon-sm5502.c - Silicon Mitus SM5502 extcon drvier to support USB switches 4914b881fSChanwoo Choi * 5914b881fSChanwoo Choi * Copyright (c) 2014 Samsung Electronics Co., Ltd 6914b881fSChanwoo Choi * Author: Chanwoo Choi <cw00.choi@samsung.com> 7914b881fSChanwoo Choi */ 8914b881fSChanwoo Choi 9914b881fSChanwoo Choi #include <linux/err.h> 10914b881fSChanwoo Choi #include <linux/i2c.h> 11914b881fSChanwoo Choi #include <linux/interrupt.h> 12914b881fSChanwoo Choi #include <linux/irqdomain.h> 13914b881fSChanwoo Choi #include <linux/kernel.h> 14914b881fSChanwoo Choi #include <linux/module.h> 15914b881fSChanwoo Choi #include <linux/platform_device.h> 16914b881fSChanwoo Choi #include <linux/regmap.h> 17914b881fSChanwoo Choi #include <linux/slab.h> 18176aa360SChanwoo Choi #include <linux/extcon-provider.h> 19ca2a07e4SChanwoo Choi 20ca2a07e4SChanwoo Choi #include "extcon-sm5502.h" 21914b881fSChanwoo Choi 22e1954452SChanwoo Choi #define DELAY_MS_DEFAULT 17000 /* unit: millisecond */ 23e1954452SChanwoo Choi 24914b881fSChanwoo Choi struct muic_irq { 25914b881fSChanwoo Choi unsigned int irq; 26914b881fSChanwoo Choi const char *name; 27914b881fSChanwoo Choi unsigned int virq; 28914b881fSChanwoo Choi }; 29914b881fSChanwoo Choi 30914b881fSChanwoo Choi struct reg_data { 31914b881fSChanwoo Choi u8 reg; 32914b881fSChanwoo Choi unsigned int val; 33914b881fSChanwoo Choi bool invert; 34914b881fSChanwoo Choi }; 35914b881fSChanwoo Choi 36914b881fSChanwoo Choi struct sm5502_muic_info { 37914b881fSChanwoo Choi struct device *dev; 38914b881fSChanwoo Choi struct extcon_dev *edev; 39914b881fSChanwoo Choi 40914b881fSChanwoo Choi struct i2c_client *i2c; 41914b881fSChanwoo Choi struct regmap *regmap; 42914b881fSChanwoo Choi 43914b881fSChanwoo Choi struct regmap_irq_chip_data *irq_data; 44914b881fSChanwoo Choi struct muic_irq *muic_irqs; 45914b881fSChanwoo Choi unsigned int num_muic_irqs; 46914b881fSChanwoo Choi int irq; 47914b881fSChanwoo Choi bool irq_attach; 48914b881fSChanwoo Choi bool irq_detach; 49914b881fSChanwoo Choi struct work_struct irq_work; 50914b881fSChanwoo Choi 51914b881fSChanwoo Choi struct reg_data *reg_data; 52914b881fSChanwoo Choi unsigned int num_reg_data; 53914b881fSChanwoo Choi 54914b881fSChanwoo Choi struct mutex mutex; 55e1954452SChanwoo Choi 56e1954452SChanwoo Choi /* 57e1954452SChanwoo Choi * Use delayed workqueue to detect cable state and then 58e1954452SChanwoo Choi * notify cable state to notifiee/platform through uevent. 59e1954452SChanwoo Choi * After completing the booting of platform, the extcon provider 60e1954452SChanwoo Choi * driver should notify cable state to upper layer. 61e1954452SChanwoo Choi */ 62e1954452SChanwoo Choi struct delayed_work wq_detcable; 63914b881fSChanwoo Choi }; 64914b881fSChanwoo Choi 65914b881fSChanwoo Choi /* Default value of SM5502 register to bring up MUIC device. */ 66914b881fSChanwoo Choi static struct reg_data sm5502_reg_data[] = { 67914b881fSChanwoo Choi { 6869426350SStephan Gerhold .reg = SM5502_REG_RESET, 6969426350SStephan Gerhold .val = SM5502_REG_RESET_MASK, 7069426350SStephan Gerhold .invert = true, 7169426350SStephan Gerhold }, { 72914b881fSChanwoo Choi .reg = SM5502_REG_CONTROL, 73914b881fSChanwoo Choi .val = SM5502_REG_CONTROL_MASK_INT_MASK, 74914b881fSChanwoo Choi .invert = false, 75914b881fSChanwoo Choi }, { 76914b881fSChanwoo Choi .reg = SM5502_REG_INTMASK1, 77914b881fSChanwoo Choi .val = SM5502_REG_INTM1_KP_MASK 78914b881fSChanwoo Choi | SM5502_REG_INTM1_LKP_MASK 79914b881fSChanwoo Choi | SM5502_REG_INTM1_LKR_MASK, 80914b881fSChanwoo Choi .invert = true, 81914b881fSChanwoo Choi }, { 82914b881fSChanwoo Choi .reg = SM5502_REG_INTMASK2, 83914b881fSChanwoo Choi .val = SM5502_REG_INTM2_VBUS_DET_MASK 84914b881fSChanwoo Choi | SM5502_REG_INTM2_REV_ACCE_MASK 85914b881fSChanwoo Choi | SM5502_REG_INTM2_ADC_CHG_MASK 86914b881fSChanwoo Choi | SM5502_REG_INTM2_STUCK_KEY_MASK 87914b881fSChanwoo Choi | SM5502_REG_INTM2_STUCK_KEY_RCV_MASK 88914b881fSChanwoo Choi | SM5502_REG_INTM2_MHL_MASK, 89914b881fSChanwoo Choi .invert = true, 90914b881fSChanwoo Choi }, 91914b881fSChanwoo Choi { } 92914b881fSChanwoo Choi }; 93914b881fSChanwoo Choi 94914b881fSChanwoo Choi /* List of detectable cables */ 9573b6ecdbSChanwoo Choi static const unsigned int sm5502_extcon_cable[] = { 962a9de9c0SChanwoo Choi EXTCON_USB, 972a9de9c0SChanwoo Choi EXTCON_USB_HOST, 988b45b6a0SChanwoo Choi EXTCON_CHG_USB_SDP, 9911eecf91SChanwoo Choi EXTCON_CHG_USB_DCP, 1002a9de9c0SChanwoo Choi EXTCON_NONE, 101914b881fSChanwoo Choi }; 102914b881fSChanwoo Choi 103914b881fSChanwoo Choi /* Define supported accessory type */ 104914b881fSChanwoo Choi enum sm5502_muic_acc_type { 105914b881fSChanwoo Choi SM5502_MUIC_ADC_GROUND = 0x0, 106914b881fSChanwoo Choi SM5502_MUIC_ADC_SEND_END_BUTTON, 107914b881fSChanwoo Choi SM5502_MUIC_ADC_REMOTE_S1_BUTTON, 108914b881fSChanwoo Choi SM5502_MUIC_ADC_REMOTE_S2_BUTTON, 109914b881fSChanwoo Choi SM5502_MUIC_ADC_REMOTE_S3_BUTTON, 110914b881fSChanwoo Choi SM5502_MUIC_ADC_REMOTE_S4_BUTTON, 111914b881fSChanwoo Choi SM5502_MUIC_ADC_REMOTE_S5_BUTTON, 112914b881fSChanwoo Choi SM5502_MUIC_ADC_REMOTE_S6_BUTTON, 113914b881fSChanwoo Choi SM5502_MUIC_ADC_REMOTE_S7_BUTTON, 114914b881fSChanwoo Choi SM5502_MUIC_ADC_REMOTE_S8_BUTTON, 115914b881fSChanwoo Choi SM5502_MUIC_ADC_REMOTE_S9_BUTTON, 116914b881fSChanwoo Choi SM5502_MUIC_ADC_REMOTE_S10_BUTTON, 117914b881fSChanwoo Choi SM5502_MUIC_ADC_REMOTE_S11_BUTTON, 118914b881fSChanwoo Choi SM5502_MUIC_ADC_REMOTE_S12_BUTTON, 119914b881fSChanwoo Choi SM5502_MUIC_ADC_RESERVED_ACC_1, 120914b881fSChanwoo Choi SM5502_MUIC_ADC_RESERVED_ACC_2, 121914b881fSChanwoo Choi SM5502_MUIC_ADC_RESERVED_ACC_3, 122914b881fSChanwoo Choi SM5502_MUIC_ADC_RESERVED_ACC_4, 123914b881fSChanwoo Choi SM5502_MUIC_ADC_RESERVED_ACC_5, 124914b881fSChanwoo Choi SM5502_MUIC_ADC_AUDIO_TYPE2, 125914b881fSChanwoo Choi SM5502_MUIC_ADC_PHONE_POWERED_DEV, 126914b881fSChanwoo Choi SM5502_MUIC_ADC_TTY_CONVERTER, 127914b881fSChanwoo Choi SM5502_MUIC_ADC_UART_CABLE, 128914b881fSChanwoo Choi SM5502_MUIC_ADC_TYPE1_CHARGER, 129914b881fSChanwoo Choi SM5502_MUIC_ADC_FACTORY_MODE_BOOT_OFF_USB, 130914b881fSChanwoo Choi SM5502_MUIC_ADC_FACTORY_MODE_BOOT_ON_USB, 131914b881fSChanwoo Choi SM5502_MUIC_ADC_AUDIO_VIDEO_CABLE, 132914b881fSChanwoo Choi SM5502_MUIC_ADC_TYPE2_CHARGER, 133914b881fSChanwoo Choi SM5502_MUIC_ADC_FACTORY_MODE_BOOT_OFF_UART, 134914b881fSChanwoo Choi SM5502_MUIC_ADC_FACTORY_MODE_BOOT_ON_UART, 135914b881fSChanwoo Choi SM5502_MUIC_ADC_AUDIO_TYPE1, 136914b881fSChanwoo Choi SM5502_MUIC_ADC_OPEN = 0x1f, 137914b881fSChanwoo Choi 138af57fa4dSSrikant Ritolia /* 139af57fa4dSSrikant Ritolia * The below accessories have same ADC value (0x1f or 0x1e). 140af57fa4dSSrikant Ritolia * So, Device type1 is used to separate specific accessory. 141af57fa4dSSrikant Ritolia */ 142914b881fSChanwoo Choi /* |---------|--ADC| */ 143914b881fSChanwoo Choi /* | [7:5]|[4:0]| */ 144914b881fSChanwoo Choi SM5502_MUIC_ADC_AUDIO_TYPE1_FULL_REMOTE = 0x3e, /* | 001|11110| */ 145914b881fSChanwoo Choi SM5502_MUIC_ADC_AUDIO_TYPE1_SEND_END = 0x5e, /* | 010|11110| */ 146914b881fSChanwoo Choi /* |Dev Type1|--ADC| */ 147*e3f60329SNikita Travkin SM5502_MUIC_ADC_GROUND_USB_OTG = 0x80, /* | 100|00000| */ 148914b881fSChanwoo Choi SM5502_MUIC_ADC_OPEN_USB = 0x5f, /* | 010|11111| */ 149914b881fSChanwoo Choi SM5502_MUIC_ADC_OPEN_TA = 0xdf, /* | 110|11111| */ 150914b881fSChanwoo Choi SM5502_MUIC_ADC_OPEN_USB_OTG = 0xff, /* | 111|11111| */ 151914b881fSChanwoo Choi }; 152914b881fSChanwoo Choi 153914b881fSChanwoo Choi /* List of supported interrupt for SM5502 */ 154914b881fSChanwoo Choi static struct muic_irq sm5502_muic_irqs[] = { 155914b881fSChanwoo Choi { SM5502_IRQ_INT1_ATTACH, "muic-attach" }, 156914b881fSChanwoo Choi { SM5502_IRQ_INT1_DETACH, "muic-detach" }, 157914b881fSChanwoo Choi { SM5502_IRQ_INT1_KP, "muic-kp" }, 158914b881fSChanwoo Choi { SM5502_IRQ_INT1_LKP, "muic-lkp" }, 159914b881fSChanwoo Choi { SM5502_IRQ_INT1_LKR, "muic-lkr" }, 160914b881fSChanwoo Choi { SM5502_IRQ_INT1_OVP_EVENT, "muic-ovp-event" }, 161914b881fSChanwoo Choi { SM5502_IRQ_INT1_OCP_EVENT, "muic-ocp-event" }, 162914b881fSChanwoo Choi { SM5502_IRQ_INT1_OVP_OCP_DIS, "muic-ovp-ocp-dis" }, 163914b881fSChanwoo Choi { SM5502_IRQ_INT2_VBUS_DET, "muic-vbus-det" }, 164914b881fSChanwoo Choi { SM5502_IRQ_INT2_REV_ACCE, "muic-rev-acce" }, 165914b881fSChanwoo Choi { SM5502_IRQ_INT2_ADC_CHG, "muic-adc-chg" }, 166914b881fSChanwoo Choi { SM5502_IRQ_INT2_STUCK_KEY, "muic-stuck-key" }, 167914b881fSChanwoo Choi { SM5502_IRQ_INT2_STUCK_KEY_RCV, "muic-stuck-key-rcv" }, 168914b881fSChanwoo Choi { SM5502_IRQ_INT2_MHL, "muic-mhl" }, 169914b881fSChanwoo Choi }; 170914b881fSChanwoo Choi 171914b881fSChanwoo Choi /* Define interrupt list of SM5502 to register regmap_irq */ 172914b881fSChanwoo Choi static const struct regmap_irq sm5502_irqs[] = { 173914b881fSChanwoo Choi /* INT1 interrupts */ 174914b881fSChanwoo Choi { .reg_offset = 0, .mask = SM5502_IRQ_INT1_ATTACH_MASK, }, 175914b881fSChanwoo Choi { .reg_offset = 0, .mask = SM5502_IRQ_INT1_DETACH_MASK, }, 176914b881fSChanwoo Choi { .reg_offset = 0, .mask = SM5502_IRQ_INT1_KP_MASK, }, 177914b881fSChanwoo Choi { .reg_offset = 0, .mask = SM5502_IRQ_INT1_LKP_MASK, }, 178914b881fSChanwoo Choi { .reg_offset = 0, .mask = SM5502_IRQ_INT1_LKR_MASK, }, 179914b881fSChanwoo Choi { .reg_offset = 0, .mask = SM5502_IRQ_INT1_OVP_EVENT_MASK, }, 180914b881fSChanwoo Choi { .reg_offset = 0, .mask = SM5502_IRQ_INT1_OCP_EVENT_MASK, }, 181914b881fSChanwoo Choi { .reg_offset = 0, .mask = SM5502_IRQ_INT1_OVP_OCP_DIS_MASK, }, 182914b881fSChanwoo Choi 183914b881fSChanwoo Choi /* INT2 interrupts */ 184914b881fSChanwoo Choi { .reg_offset = 1, .mask = SM5502_IRQ_INT2_VBUS_DET_MASK,}, 185914b881fSChanwoo Choi { .reg_offset = 1, .mask = SM5502_IRQ_INT2_REV_ACCE_MASK, }, 186914b881fSChanwoo Choi { .reg_offset = 1, .mask = SM5502_IRQ_INT2_ADC_CHG_MASK, }, 187914b881fSChanwoo Choi { .reg_offset = 1, .mask = SM5502_IRQ_INT2_STUCK_KEY_MASK, }, 188914b881fSChanwoo Choi { .reg_offset = 1, .mask = SM5502_IRQ_INT2_STUCK_KEY_RCV_MASK, }, 189914b881fSChanwoo Choi { .reg_offset = 1, .mask = SM5502_IRQ_INT2_MHL_MASK, }, 190914b881fSChanwoo Choi }; 191914b881fSChanwoo Choi 192914b881fSChanwoo Choi static const struct regmap_irq_chip sm5502_muic_irq_chip = { 193914b881fSChanwoo Choi .name = "sm5502", 194914b881fSChanwoo Choi .status_base = SM5502_REG_INT1, 195914b881fSChanwoo Choi .mask_base = SM5502_REG_INTMASK1, 196914b881fSChanwoo Choi .mask_invert = false, 197914b881fSChanwoo Choi .num_regs = 2, 198914b881fSChanwoo Choi .irqs = sm5502_irqs, 199914b881fSChanwoo Choi .num_irqs = ARRAY_SIZE(sm5502_irqs), 200914b881fSChanwoo Choi }; 201914b881fSChanwoo Choi 202914b881fSChanwoo Choi /* Define regmap configuration of SM5502 for I2C communication */ 203914b881fSChanwoo Choi static bool sm5502_muic_volatile_reg(struct device *dev, unsigned int reg) 204914b881fSChanwoo Choi { 205914b881fSChanwoo Choi switch (reg) { 206914b881fSChanwoo Choi case SM5502_REG_INTMASK1: 207914b881fSChanwoo Choi case SM5502_REG_INTMASK2: 208914b881fSChanwoo Choi return true; 209914b881fSChanwoo Choi default: 210914b881fSChanwoo Choi break; 211914b881fSChanwoo Choi } 212914b881fSChanwoo Choi return false; 213914b881fSChanwoo Choi } 214914b881fSChanwoo Choi 215914b881fSChanwoo Choi static const struct regmap_config sm5502_muic_regmap_config = { 216914b881fSChanwoo Choi .reg_bits = 8, 217914b881fSChanwoo Choi .val_bits = 8, 218914b881fSChanwoo Choi .volatile_reg = sm5502_muic_volatile_reg, 219914b881fSChanwoo Choi .max_register = SM5502_REG_END, 220914b881fSChanwoo Choi }; 221914b881fSChanwoo Choi 222a75fed2eSChanwoo Choi /* Change DM_CON/DP_CON/VBUSIN switch according to cable type */ 223a75fed2eSChanwoo Choi static int sm5502_muic_set_path(struct sm5502_muic_info *info, 224a75fed2eSChanwoo Choi unsigned int con_sw, unsigned int vbus_sw, 225a75fed2eSChanwoo Choi bool attached) 226a75fed2eSChanwoo Choi { 227a75fed2eSChanwoo Choi int ret; 228a75fed2eSChanwoo Choi 229a75fed2eSChanwoo Choi if (!attached) { 230a75fed2eSChanwoo Choi con_sw = DM_DP_SWITCH_OPEN; 231a75fed2eSChanwoo Choi vbus_sw = VBUSIN_SWITCH_OPEN; 232a75fed2eSChanwoo Choi } 233a75fed2eSChanwoo Choi 234a75fed2eSChanwoo Choi switch (con_sw) { 235a75fed2eSChanwoo Choi case DM_DP_SWITCH_OPEN: 236a75fed2eSChanwoo Choi case DM_DP_SWITCH_USB: 237a75fed2eSChanwoo Choi case DM_DP_SWITCH_AUDIO: 238a75fed2eSChanwoo Choi case DM_DP_SWITCH_UART: 239a75fed2eSChanwoo Choi ret = regmap_update_bits(info->regmap, SM5502_REG_MANUAL_SW1, 240a75fed2eSChanwoo Choi SM5502_REG_MANUAL_SW1_DP_MASK | 241a75fed2eSChanwoo Choi SM5502_REG_MANUAL_SW1_DM_MASK, 242a75fed2eSChanwoo Choi con_sw); 243a75fed2eSChanwoo Choi if (ret < 0) { 244a75fed2eSChanwoo Choi dev_err(info->dev, 245a75fed2eSChanwoo Choi "cannot update DM_CON/DP_CON switch\n"); 246a75fed2eSChanwoo Choi return ret; 247a75fed2eSChanwoo Choi } 248a75fed2eSChanwoo Choi break; 249a75fed2eSChanwoo Choi default: 250a75fed2eSChanwoo Choi dev_err(info->dev, "Unknown DM_CON/DP_CON switch type (%d)\n", 251a75fed2eSChanwoo Choi con_sw); 252a75fed2eSChanwoo Choi return -EINVAL; 2532ddf50a7SXu Wang } 254a75fed2eSChanwoo Choi 255a75fed2eSChanwoo Choi switch (vbus_sw) { 256a75fed2eSChanwoo Choi case VBUSIN_SWITCH_OPEN: 257a75fed2eSChanwoo Choi case VBUSIN_SWITCH_VBUSOUT: 258a75fed2eSChanwoo Choi case VBUSIN_SWITCH_MIC: 259a75fed2eSChanwoo Choi case VBUSIN_SWITCH_VBUSOUT_WITH_USB: 260a75fed2eSChanwoo Choi ret = regmap_update_bits(info->regmap, SM5502_REG_MANUAL_SW1, 261a75fed2eSChanwoo Choi SM5502_REG_MANUAL_SW1_VBUSIN_MASK, 262a75fed2eSChanwoo Choi vbus_sw); 263a75fed2eSChanwoo Choi if (ret < 0) { 264a75fed2eSChanwoo Choi dev_err(info->dev, 265a75fed2eSChanwoo Choi "cannot update VBUSIN switch\n"); 266a75fed2eSChanwoo Choi return ret; 267a75fed2eSChanwoo Choi } 268a75fed2eSChanwoo Choi break; 269a75fed2eSChanwoo Choi default: 270a75fed2eSChanwoo Choi dev_err(info->dev, "Unknown VBUS switch type (%d)\n", vbus_sw); 271a75fed2eSChanwoo Choi return -EINVAL; 2722ddf50a7SXu Wang } 273a75fed2eSChanwoo Choi 274a75fed2eSChanwoo Choi return 0; 275a75fed2eSChanwoo Choi } 276a75fed2eSChanwoo Choi 277914b881fSChanwoo Choi /* Return cable type of attached or detached accessories */ 278914b881fSChanwoo Choi static unsigned int sm5502_muic_get_cable_type(struct sm5502_muic_info *info) 279914b881fSChanwoo Choi { 280ddd1bbbaSColin Ian King unsigned int cable_type, adc, dev_type1; 281914b881fSChanwoo Choi int ret; 282914b881fSChanwoo Choi 283914b881fSChanwoo Choi /* Read ADC value according to external cable or button */ 284914b881fSChanwoo Choi ret = regmap_read(info->regmap, SM5502_REG_ADC, &adc); 285914b881fSChanwoo Choi if (ret) { 286914b881fSChanwoo Choi dev_err(info->dev, "failed to read ADC register\n"); 287914b881fSChanwoo Choi return ret; 288914b881fSChanwoo Choi } 289914b881fSChanwoo Choi 290914b881fSChanwoo Choi /* 291914b881fSChanwoo Choi * If ADC is SM5502_MUIC_ADC_GROUND(0x0), external cable hasn't 292914b881fSChanwoo Choi * connected with to MUIC device. 293914b881fSChanwoo Choi */ 2940ccc7955SChanwoo Choi cable_type = adc & SM5502_REG_ADC_MASK; 295914b881fSChanwoo Choi 296914b881fSChanwoo Choi switch (cable_type) { 297914b881fSChanwoo Choi case SM5502_MUIC_ADC_GROUND: 298*e3f60329SNikita Travkin ret = regmap_read(info->regmap, SM5502_REG_DEV_TYPE1, 299*e3f60329SNikita Travkin &dev_type1); 300*e3f60329SNikita Travkin if (ret) { 301*e3f60329SNikita Travkin dev_err(info->dev, "failed to read DEV_TYPE1 reg\n"); 302*e3f60329SNikita Travkin return ret; 303*e3f60329SNikita Travkin } 304*e3f60329SNikita Travkin 305*e3f60329SNikita Travkin switch (dev_type1) { 306*e3f60329SNikita Travkin case SM5502_REG_DEV_TYPE1_USB_OTG_MASK: 307*e3f60329SNikita Travkin cable_type = SM5502_MUIC_ADC_GROUND_USB_OTG; 308*e3f60329SNikita Travkin break; 309*e3f60329SNikita Travkin default: 310*e3f60329SNikita Travkin dev_dbg(info->dev, 311*e3f60329SNikita Travkin "cannot identify the cable type: adc(0x%x), dev_type1(0x%x)\n", 312*e3f60329SNikita Travkin adc, dev_type1); 313*e3f60329SNikita Travkin return -EINVAL; 314*e3f60329SNikita Travkin } 315*e3f60329SNikita Travkin break; 316914b881fSChanwoo Choi case SM5502_MUIC_ADC_SEND_END_BUTTON: 317914b881fSChanwoo Choi case SM5502_MUIC_ADC_REMOTE_S1_BUTTON: 318914b881fSChanwoo Choi case SM5502_MUIC_ADC_REMOTE_S2_BUTTON: 319914b881fSChanwoo Choi case SM5502_MUIC_ADC_REMOTE_S3_BUTTON: 320914b881fSChanwoo Choi case SM5502_MUIC_ADC_REMOTE_S4_BUTTON: 321914b881fSChanwoo Choi case SM5502_MUIC_ADC_REMOTE_S5_BUTTON: 322914b881fSChanwoo Choi case SM5502_MUIC_ADC_REMOTE_S6_BUTTON: 323914b881fSChanwoo Choi case SM5502_MUIC_ADC_REMOTE_S7_BUTTON: 324914b881fSChanwoo Choi case SM5502_MUIC_ADC_REMOTE_S8_BUTTON: 325914b881fSChanwoo Choi case SM5502_MUIC_ADC_REMOTE_S9_BUTTON: 326914b881fSChanwoo Choi case SM5502_MUIC_ADC_REMOTE_S10_BUTTON: 327914b881fSChanwoo Choi case SM5502_MUIC_ADC_REMOTE_S11_BUTTON: 328914b881fSChanwoo Choi case SM5502_MUIC_ADC_REMOTE_S12_BUTTON: 329914b881fSChanwoo Choi case SM5502_MUIC_ADC_RESERVED_ACC_1: 330914b881fSChanwoo Choi case SM5502_MUIC_ADC_RESERVED_ACC_2: 331914b881fSChanwoo Choi case SM5502_MUIC_ADC_RESERVED_ACC_3: 332914b881fSChanwoo Choi case SM5502_MUIC_ADC_RESERVED_ACC_4: 333914b881fSChanwoo Choi case SM5502_MUIC_ADC_RESERVED_ACC_5: 334914b881fSChanwoo Choi case SM5502_MUIC_ADC_AUDIO_TYPE2: 335914b881fSChanwoo Choi case SM5502_MUIC_ADC_PHONE_POWERED_DEV: 336914b881fSChanwoo Choi case SM5502_MUIC_ADC_TTY_CONVERTER: 337914b881fSChanwoo Choi case SM5502_MUIC_ADC_UART_CABLE: 338914b881fSChanwoo Choi case SM5502_MUIC_ADC_TYPE1_CHARGER: 339914b881fSChanwoo Choi case SM5502_MUIC_ADC_FACTORY_MODE_BOOT_OFF_USB: 340914b881fSChanwoo Choi case SM5502_MUIC_ADC_FACTORY_MODE_BOOT_ON_USB: 341914b881fSChanwoo Choi case SM5502_MUIC_ADC_AUDIO_VIDEO_CABLE: 342914b881fSChanwoo Choi case SM5502_MUIC_ADC_TYPE2_CHARGER: 343914b881fSChanwoo Choi case SM5502_MUIC_ADC_FACTORY_MODE_BOOT_OFF_UART: 344914b881fSChanwoo Choi case SM5502_MUIC_ADC_FACTORY_MODE_BOOT_ON_UART: 345914b881fSChanwoo Choi break; 346914b881fSChanwoo Choi case SM5502_MUIC_ADC_AUDIO_TYPE1: 347914b881fSChanwoo Choi /* 348914b881fSChanwoo Choi * Check whether cable type is 349914b881fSChanwoo Choi * SM5502_MUIC_ADC_AUDIO_TYPE1_FULL_REMOTE 350914b881fSChanwoo Choi * or SM5502_MUIC_ADC_AUDIO_TYPE1_SEND_END 351914b881fSChanwoo Choi * by using Button event. 352914b881fSChanwoo Choi */ 353914b881fSChanwoo Choi break; 354914b881fSChanwoo Choi case SM5502_MUIC_ADC_OPEN: 355914b881fSChanwoo Choi ret = regmap_read(info->regmap, SM5502_REG_DEV_TYPE1, 356914b881fSChanwoo Choi &dev_type1); 357914b881fSChanwoo Choi if (ret) { 358914b881fSChanwoo Choi dev_err(info->dev, "failed to read DEV_TYPE1 reg\n"); 359914b881fSChanwoo Choi return ret; 360914b881fSChanwoo Choi } 361914b881fSChanwoo Choi 362914b881fSChanwoo Choi switch (dev_type1) { 363914b881fSChanwoo Choi case SM5502_REG_DEV_TYPE1_USB_SDP_MASK: 364914b881fSChanwoo Choi cable_type = SM5502_MUIC_ADC_OPEN_USB; 365914b881fSChanwoo Choi break; 366914b881fSChanwoo Choi case SM5502_REG_DEV_TYPE1_DEDICATED_CHG_MASK: 367914b881fSChanwoo Choi cable_type = SM5502_MUIC_ADC_OPEN_TA; 368914b881fSChanwoo Choi break; 369914b881fSChanwoo Choi case SM5502_REG_DEV_TYPE1_USB_OTG_MASK: 370914b881fSChanwoo Choi cable_type = SM5502_MUIC_ADC_OPEN_USB_OTG; 371914b881fSChanwoo Choi break; 372914b881fSChanwoo Choi default: 373914b881fSChanwoo Choi dev_dbg(info->dev, 37434825e51SChanwoo Choi "cannot identify the cable type: adc(0x%x)\n", 37534825e51SChanwoo Choi adc); 376914b881fSChanwoo Choi return -EINVAL; 3772ddf50a7SXu Wang } 378914b881fSChanwoo Choi break; 379914b881fSChanwoo Choi default: 380914b881fSChanwoo Choi dev_err(info->dev, 381914b881fSChanwoo Choi "failed to identify the cable type: adc(0x%x)\n", adc); 382914b881fSChanwoo Choi return -EINVAL; 3832ddf50a7SXu Wang } 384914b881fSChanwoo Choi 385914b881fSChanwoo Choi return cable_type; 386914b881fSChanwoo Choi } 387914b881fSChanwoo Choi 388914b881fSChanwoo Choi static int sm5502_muic_cable_handler(struct sm5502_muic_info *info, 389914b881fSChanwoo Choi bool attached) 390914b881fSChanwoo Choi { 391914b881fSChanwoo Choi static unsigned int prev_cable_type = SM5502_MUIC_ADC_GROUND; 392914b881fSChanwoo Choi unsigned int cable_type = SM5502_MUIC_ADC_GROUND; 393a75fed2eSChanwoo Choi unsigned int con_sw = DM_DP_SWITCH_OPEN; 394a75fed2eSChanwoo Choi unsigned int vbus_sw = VBUSIN_SWITCH_OPEN; 39573b6ecdbSChanwoo Choi unsigned int id; 396a75fed2eSChanwoo Choi int ret; 397914b881fSChanwoo Choi 398914b881fSChanwoo Choi /* Get the type of attached or detached cable */ 399914b881fSChanwoo Choi if (attached) 400914b881fSChanwoo Choi cable_type = sm5502_muic_get_cable_type(info); 401fbae30d8SChanwoo Choi else 402914b881fSChanwoo Choi cable_type = prev_cable_type; 403914b881fSChanwoo Choi prev_cable_type = cable_type; 404914b881fSChanwoo Choi 405914b881fSChanwoo Choi switch (cable_type) { 406914b881fSChanwoo Choi case SM5502_MUIC_ADC_OPEN_USB: 4072a9de9c0SChanwoo Choi id = EXTCON_USB; 408a75fed2eSChanwoo Choi con_sw = DM_DP_SWITCH_USB; 409a75fed2eSChanwoo Choi vbus_sw = VBUSIN_SWITCH_VBUSOUT_WITH_USB; 410914b881fSChanwoo Choi break; 411914b881fSChanwoo Choi case SM5502_MUIC_ADC_OPEN_TA: 41211eecf91SChanwoo Choi id = EXTCON_CHG_USB_DCP; 413a75fed2eSChanwoo Choi con_sw = DM_DP_SWITCH_OPEN; 414a75fed2eSChanwoo Choi vbus_sw = VBUSIN_SWITCH_VBUSOUT; 415914b881fSChanwoo Choi break; 416*e3f60329SNikita Travkin case SM5502_MUIC_ADC_GROUND_USB_OTG: 417914b881fSChanwoo Choi case SM5502_MUIC_ADC_OPEN_USB_OTG: 4182a9de9c0SChanwoo Choi id = EXTCON_USB_HOST; 419a75fed2eSChanwoo Choi con_sw = DM_DP_SWITCH_USB; 420a75fed2eSChanwoo Choi vbus_sw = VBUSIN_SWITCH_OPEN; 421e1954452SChanwoo Choi break; 422914b881fSChanwoo Choi default: 423914b881fSChanwoo Choi dev_dbg(info->dev, 424914b881fSChanwoo Choi "cannot handle this cable_type (0x%x)\n", cable_type); 425914b881fSChanwoo Choi return 0; 4262ddf50a7SXu Wang } 427914b881fSChanwoo Choi 428a75fed2eSChanwoo Choi /* Change internal hardware path(DM_CON/DP_CON, VBUSIN) */ 429a75fed2eSChanwoo Choi ret = sm5502_muic_set_path(info, con_sw, vbus_sw, attached); 430a75fed2eSChanwoo Choi if (ret < 0) 431a75fed2eSChanwoo Choi return ret; 432a75fed2eSChanwoo Choi 433a75fed2eSChanwoo Choi /* Change the state of external accessory */ 4348670b459SChanwoo Choi extcon_set_state_sync(info->edev, id, attached); 4358b45b6a0SChanwoo Choi if (id == EXTCON_USB) 4368670b459SChanwoo Choi extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SDP, 4378b45b6a0SChanwoo Choi attached); 438914b881fSChanwoo Choi 439914b881fSChanwoo Choi return 0; 440914b881fSChanwoo Choi } 441914b881fSChanwoo Choi 442914b881fSChanwoo Choi static void sm5502_muic_irq_work(struct work_struct *work) 443914b881fSChanwoo Choi { 444914b881fSChanwoo Choi struct sm5502_muic_info *info = container_of(work, 445914b881fSChanwoo Choi struct sm5502_muic_info, irq_work); 446914b881fSChanwoo Choi int ret = 0; 447914b881fSChanwoo Choi 448914b881fSChanwoo Choi if (!info->edev) 449914b881fSChanwoo Choi return; 450914b881fSChanwoo Choi 451914b881fSChanwoo Choi mutex_lock(&info->mutex); 452914b881fSChanwoo Choi 453914b881fSChanwoo Choi /* Detect attached or detached cables */ 454914b881fSChanwoo Choi if (info->irq_attach) { 455914b881fSChanwoo Choi ret = sm5502_muic_cable_handler(info, true); 456914b881fSChanwoo Choi info->irq_attach = false; 457914b881fSChanwoo Choi } 458914b881fSChanwoo Choi if (info->irq_detach) { 459914b881fSChanwoo Choi ret = sm5502_muic_cable_handler(info, false); 460914b881fSChanwoo Choi info->irq_detach = false; 461914b881fSChanwoo Choi } 462914b881fSChanwoo Choi 463914b881fSChanwoo Choi if (ret < 0) 464914b881fSChanwoo Choi dev_err(info->dev, "failed to handle MUIC interrupt\n"); 465914b881fSChanwoo Choi 466914b881fSChanwoo Choi mutex_unlock(&info->mutex); 467914b881fSChanwoo Choi } 468914b881fSChanwoo Choi 469914b881fSChanwoo Choi /* 470914b881fSChanwoo Choi * Sets irq_attach or irq_detach in sm5502_muic_info and returns 0. 471914b881fSChanwoo Choi * Returns -ESRCH if irq_type does not match registered IRQ for this dev type. 472914b881fSChanwoo Choi */ 473914b881fSChanwoo Choi static int sm5502_parse_irq(struct sm5502_muic_info *info, int irq_type) 474914b881fSChanwoo Choi { 475914b881fSChanwoo Choi switch (irq_type) { 476914b881fSChanwoo Choi case SM5502_IRQ_INT1_ATTACH: 477914b881fSChanwoo Choi info->irq_attach = true; 478914b881fSChanwoo Choi break; 479914b881fSChanwoo Choi case SM5502_IRQ_INT1_DETACH: 480914b881fSChanwoo Choi info->irq_detach = true; 481914b881fSChanwoo Choi break; 482914b881fSChanwoo Choi case SM5502_IRQ_INT1_KP: 483914b881fSChanwoo Choi case SM5502_IRQ_INT1_LKP: 484914b881fSChanwoo Choi case SM5502_IRQ_INT1_LKR: 485914b881fSChanwoo Choi case SM5502_IRQ_INT1_OVP_EVENT: 486914b881fSChanwoo Choi case SM5502_IRQ_INT1_OCP_EVENT: 487914b881fSChanwoo Choi case SM5502_IRQ_INT1_OVP_OCP_DIS: 488914b881fSChanwoo Choi case SM5502_IRQ_INT2_VBUS_DET: 489914b881fSChanwoo Choi case SM5502_IRQ_INT2_REV_ACCE: 490914b881fSChanwoo Choi case SM5502_IRQ_INT2_ADC_CHG: 491914b881fSChanwoo Choi case SM5502_IRQ_INT2_STUCK_KEY: 492914b881fSChanwoo Choi case SM5502_IRQ_INT2_STUCK_KEY_RCV: 493914b881fSChanwoo Choi case SM5502_IRQ_INT2_MHL: 494914b881fSChanwoo Choi default: 495914b881fSChanwoo Choi break; 496914b881fSChanwoo Choi } 497914b881fSChanwoo Choi 498914b881fSChanwoo Choi return 0; 499914b881fSChanwoo Choi } 500914b881fSChanwoo Choi 501914b881fSChanwoo Choi static irqreturn_t sm5502_muic_irq_handler(int irq, void *data) 502914b881fSChanwoo Choi { 503914b881fSChanwoo Choi struct sm5502_muic_info *info = data; 504914b881fSChanwoo Choi int i, irq_type = -1, ret; 505914b881fSChanwoo Choi 506914b881fSChanwoo Choi for (i = 0; i < info->num_muic_irqs; i++) 507914b881fSChanwoo Choi if (irq == info->muic_irqs[i].virq) 508914b881fSChanwoo Choi irq_type = info->muic_irqs[i].irq; 509914b881fSChanwoo Choi 510914b881fSChanwoo Choi ret = sm5502_parse_irq(info, irq_type); 511914b881fSChanwoo Choi if (ret < 0) { 512914b881fSChanwoo Choi dev_warn(info->dev, "cannot handle is interrupt:%d\n", 513914b881fSChanwoo Choi irq_type); 514914b881fSChanwoo Choi return IRQ_HANDLED; 515914b881fSChanwoo Choi } 516914b881fSChanwoo Choi schedule_work(&info->irq_work); 517914b881fSChanwoo Choi 518914b881fSChanwoo Choi return IRQ_HANDLED; 519914b881fSChanwoo Choi } 520914b881fSChanwoo Choi 521e1954452SChanwoo Choi static void sm5502_muic_detect_cable_wq(struct work_struct *work) 522e1954452SChanwoo Choi { 523e1954452SChanwoo Choi struct sm5502_muic_info *info = container_of(to_delayed_work(work), 524e1954452SChanwoo Choi struct sm5502_muic_info, wq_detcable); 525e1954452SChanwoo Choi int ret; 526e1954452SChanwoo Choi 527e1954452SChanwoo Choi /* Notify the state of connector cable or not */ 528e1954452SChanwoo Choi ret = sm5502_muic_cable_handler(info, true); 529e1954452SChanwoo Choi if (ret < 0) 530e1954452SChanwoo Choi dev_warn(info->dev, "failed to detect cable state\n"); 531e1954452SChanwoo Choi } 532e1954452SChanwoo Choi 533914b881fSChanwoo Choi static void sm5502_init_dev_type(struct sm5502_muic_info *info) 534914b881fSChanwoo Choi { 535914b881fSChanwoo Choi unsigned int reg_data, vendor_id, version_id; 536914b881fSChanwoo Choi int i, ret; 537914b881fSChanwoo Choi 538914b881fSChanwoo Choi /* To test I2C, Print version_id and vendor_id of SM5502 */ 539914b881fSChanwoo Choi ret = regmap_read(info->regmap, SM5502_REG_DEVICE_ID, ®_data); 540914b881fSChanwoo Choi if (ret) { 541914b881fSChanwoo Choi dev_err(info->dev, 542914b881fSChanwoo Choi "failed to read DEVICE_ID register: %d\n", ret); 543914b881fSChanwoo Choi return; 544914b881fSChanwoo Choi } 545914b881fSChanwoo Choi 546914b881fSChanwoo Choi vendor_id = ((reg_data & SM5502_REG_DEVICE_ID_VENDOR_MASK) >> 547914b881fSChanwoo Choi SM5502_REG_DEVICE_ID_VENDOR_SHIFT); 548914b881fSChanwoo Choi version_id = ((reg_data & SM5502_REG_DEVICE_ID_VERSION_MASK) >> 549914b881fSChanwoo Choi SM5502_REG_DEVICE_ID_VERSION_SHIFT); 550914b881fSChanwoo Choi 551914b881fSChanwoo Choi dev_info(info->dev, "Device type: version: 0x%x, vendor: 0x%x\n", 552914b881fSChanwoo Choi version_id, vendor_id); 553914b881fSChanwoo Choi 554914b881fSChanwoo Choi /* Initiazle the register of SM5502 device to bring-up */ 555914b881fSChanwoo Choi for (i = 0; i < info->num_reg_data; i++) { 556914b881fSChanwoo Choi unsigned int val = 0; 557914b881fSChanwoo Choi 558914b881fSChanwoo Choi if (!info->reg_data[i].invert) 559914b881fSChanwoo Choi val |= ~info->reg_data[i].val; 560914b881fSChanwoo Choi else 561914b881fSChanwoo Choi val = info->reg_data[i].val; 562914b881fSChanwoo Choi regmap_write(info->regmap, info->reg_data[i].reg, val); 563914b881fSChanwoo Choi } 564914b881fSChanwoo Choi } 565914b881fSChanwoo Choi 566914b881fSChanwoo Choi static int sm5022_muic_i2c_probe(struct i2c_client *i2c, 567914b881fSChanwoo Choi const struct i2c_device_id *id) 568914b881fSChanwoo Choi { 569914b881fSChanwoo Choi struct device_node *np = i2c->dev.of_node; 570914b881fSChanwoo Choi struct sm5502_muic_info *info; 571914b881fSChanwoo Choi int i, ret, irq_flags; 572914b881fSChanwoo Choi 573914b881fSChanwoo Choi if (!np) 574914b881fSChanwoo Choi return -EINVAL; 575914b881fSChanwoo Choi 576914b881fSChanwoo Choi info = devm_kzalloc(&i2c->dev, sizeof(*info), GFP_KERNEL); 577914b881fSChanwoo Choi if (!info) 578914b881fSChanwoo Choi return -ENOMEM; 579914b881fSChanwoo Choi i2c_set_clientdata(i2c, info); 580914b881fSChanwoo Choi 581914b881fSChanwoo Choi info->dev = &i2c->dev; 582914b881fSChanwoo Choi info->i2c = i2c; 583914b881fSChanwoo Choi info->irq = i2c->irq; 584914b881fSChanwoo Choi info->muic_irqs = sm5502_muic_irqs; 585914b881fSChanwoo Choi info->num_muic_irqs = ARRAY_SIZE(sm5502_muic_irqs); 586914b881fSChanwoo Choi info->reg_data = sm5502_reg_data; 587914b881fSChanwoo Choi info->num_reg_data = ARRAY_SIZE(sm5502_reg_data); 588914b881fSChanwoo Choi 589914b881fSChanwoo Choi mutex_init(&info->mutex); 590914b881fSChanwoo Choi 591914b881fSChanwoo Choi INIT_WORK(&info->irq_work, sm5502_muic_irq_work); 592914b881fSChanwoo Choi 593914b881fSChanwoo Choi info->regmap = devm_regmap_init_i2c(i2c, &sm5502_muic_regmap_config); 594914b881fSChanwoo Choi if (IS_ERR(info->regmap)) { 595914b881fSChanwoo Choi ret = PTR_ERR(info->regmap); 596914b881fSChanwoo Choi dev_err(info->dev, "failed to allocate register map: %d\n", 597914b881fSChanwoo Choi ret); 598914b881fSChanwoo Choi return ret; 599914b881fSChanwoo Choi } 600914b881fSChanwoo Choi 601914b881fSChanwoo Choi /* Support irq domain for SM5502 MUIC device */ 602914b881fSChanwoo Choi irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT | IRQF_SHARED; 603914b881fSChanwoo Choi ret = regmap_add_irq_chip(info->regmap, info->irq, irq_flags, 0, 604914b881fSChanwoo Choi &sm5502_muic_irq_chip, &info->irq_data); 605914b881fSChanwoo Choi if (ret != 0) { 606914b881fSChanwoo Choi dev_err(info->dev, "failed to request IRQ %d: %d\n", 607914b881fSChanwoo Choi info->irq, ret); 608914b881fSChanwoo Choi return ret; 609914b881fSChanwoo Choi } 610914b881fSChanwoo Choi 611914b881fSChanwoo Choi for (i = 0; i < info->num_muic_irqs; i++) { 612914b881fSChanwoo Choi struct muic_irq *muic_irq = &info->muic_irqs[i]; 613363b3891SAndrzej Hajda int virq = 0; 614914b881fSChanwoo Choi 615914b881fSChanwoo Choi virq = regmap_irq_get_virq(info->irq_data, muic_irq->irq); 616914b881fSChanwoo Choi if (virq <= 0) 617914b881fSChanwoo Choi return -EINVAL; 618914b881fSChanwoo Choi muic_irq->virq = virq; 619914b881fSChanwoo Choi 620914b881fSChanwoo Choi ret = devm_request_threaded_irq(info->dev, virq, NULL, 621914b881fSChanwoo Choi sm5502_muic_irq_handler, 622005ad187SVasyl Gomonovych IRQF_NO_SUSPEND | IRQF_ONESHOT, 623914b881fSChanwoo Choi muic_irq->name, info); 624914b881fSChanwoo Choi if (ret) { 625fbae30d8SChanwoo Choi dev_err(info->dev, 626fbae30d8SChanwoo Choi "failed: irq request (IRQ: %d, error :%d)\n", 627fbae30d8SChanwoo Choi muic_irq->irq, ret); 628914b881fSChanwoo Choi return ret; 629914b881fSChanwoo Choi } 630914b881fSChanwoo Choi } 631914b881fSChanwoo Choi 632914b881fSChanwoo Choi /* Allocate extcon device */ 633914b881fSChanwoo Choi info->edev = devm_extcon_dev_allocate(info->dev, sm5502_extcon_cable); 634914b881fSChanwoo Choi if (IS_ERR(info->edev)) { 635914b881fSChanwoo Choi dev_err(info->dev, "failed to allocate memory for extcon\n"); 636914b881fSChanwoo Choi return -ENOMEM; 637914b881fSChanwoo Choi } 638914b881fSChanwoo Choi 639914b881fSChanwoo Choi /* Register extcon device */ 640914b881fSChanwoo Choi ret = devm_extcon_dev_register(info->dev, info->edev); 641914b881fSChanwoo Choi if (ret) { 642914b881fSChanwoo Choi dev_err(info->dev, "failed to register extcon device\n"); 643914b881fSChanwoo Choi return ret; 644914b881fSChanwoo Choi } 645914b881fSChanwoo Choi 646e1954452SChanwoo Choi /* 647e1954452SChanwoo Choi * Detect accessory after completing the initialization of platform 648e1954452SChanwoo Choi * 649e1954452SChanwoo Choi * - Use delayed workqueue to detect cable state and then 650e1954452SChanwoo Choi * notify cable state to notifiee/platform through uevent. 651e1954452SChanwoo Choi * After completing the booting of platform, the extcon provider 652e1954452SChanwoo Choi * driver should notify cable state to upper layer. 653e1954452SChanwoo Choi */ 654e1954452SChanwoo Choi INIT_DELAYED_WORK(&info->wq_detcable, sm5502_muic_detect_cable_wq); 655e1954452SChanwoo Choi queue_delayed_work(system_power_efficient_wq, &info->wq_detcable, 656e1954452SChanwoo Choi msecs_to_jiffies(DELAY_MS_DEFAULT)); 657e1954452SChanwoo Choi 658914b881fSChanwoo Choi /* Initialize SM5502 device and print vendor id and version id */ 659914b881fSChanwoo Choi sm5502_init_dev_type(info); 660914b881fSChanwoo Choi 661914b881fSChanwoo Choi return 0; 662914b881fSChanwoo Choi } 663914b881fSChanwoo Choi 664914b881fSChanwoo Choi static int sm5502_muic_i2c_remove(struct i2c_client *i2c) 665914b881fSChanwoo Choi { 666914b881fSChanwoo Choi struct sm5502_muic_info *info = i2c_get_clientdata(i2c); 667914b881fSChanwoo Choi 668914b881fSChanwoo Choi regmap_del_irq_chip(info->irq, info->irq_data); 669914b881fSChanwoo Choi 670914b881fSChanwoo Choi return 0; 671914b881fSChanwoo Choi } 672914b881fSChanwoo Choi 67334825e51SChanwoo Choi static const struct of_device_id sm5502_dt_match[] = { 674914b881fSChanwoo Choi { .compatible = "siliconmitus,sm5502-muic" }, 675914b881fSChanwoo Choi { }, 676914b881fSChanwoo Choi }; 677ff612f91SJavier Martinez Canillas MODULE_DEVICE_TABLE(of, sm5502_dt_match); 678914b881fSChanwoo Choi 679914b881fSChanwoo Choi #ifdef CONFIG_PM_SLEEP 680914b881fSChanwoo Choi static int sm5502_muic_suspend(struct device *dev) 681914b881fSChanwoo Choi { 682d5859342SGeliang Tang struct i2c_client *i2c = to_i2c_client(dev); 683914b881fSChanwoo Choi struct sm5502_muic_info *info = i2c_get_clientdata(i2c); 684914b881fSChanwoo Choi 685914b881fSChanwoo Choi enable_irq_wake(info->irq); 686914b881fSChanwoo Choi 687914b881fSChanwoo Choi return 0; 688914b881fSChanwoo Choi } 689914b881fSChanwoo Choi 690914b881fSChanwoo Choi static int sm5502_muic_resume(struct device *dev) 691914b881fSChanwoo Choi { 692d5859342SGeliang Tang struct i2c_client *i2c = to_i2c_client(dev); 693914b881fSChanwoo Choi struct sm5502_muic_info *info = i2c_get_clientdata(i2c); 694914b881fSChanwoo Choi 695914b881fSChanwoo Choi disable_irq_wake(info->irq); 696914b881fSChanwoo Choi 697914b881fSChanwoo Choi return 0; 698914b881fSChanwoo Choi } 699914b881fSChanwoo Choi #endif 700914b881fSChanwoo Choi 701914b881fSChanwoo Choi static SIMPLE_DEV_PM_OPS(sm5502_muic_pm_ops, 702914b881fSChanwoo Choi sm5502_muic_suspend, sm5502_muic_resume); 703914b881fSChanwoo Choi 704914b881fSChanwoo Choi static const struct i2c_device_id sm5502_i2c_id[] = { 705914b881fSChanwoo Choi { "sm5502", TYPE_SM5502 }, 706914b881fSChanwoo Choi { } 707914b881fSChanwoo Choi }; 708914b881fSChanwoo Choi MODULE_DEVICE_TABLE(i2c, sm5502_i2c_id); 709914b881fSChanwoo Choi 710914b881fSChanwoo Choi static struct i2c_driver sm5502_muic_i2c_driver = { 711914b881fSChanwoo Choi .driver = { 712914b881fSChanwoo Choi .name = "sm5502", 713914b881fSChanwoo Choi .pm = &sm5502_muic_pm_ops, 714914b881fSChanwoo Choi .of_match_table = sm5502_dt_match, 715914b881fSChanwoo Choi }, 716914b881fSChanwoo Choi .probe = sm5022_muic_i2c_probe, 717914b881fSChanwoo Choi .remove = sm5502_muic_i2c_remove, 718914b881fSChanwoo Choi .id_table = sm5502_i2c_id, 719914b881fSChanwoo Choi }; 720914b881fSChanwoo Choi 721914b881fSChanwoo Choi static int __init sm5502_muic_i2c_init(void) 722914b881fSChanwoo Choi { 723914b881fSChanwoo Choi return i2c_add_driver(&sm5502_muic_i2c_driver); 724914b881fSChanwoo Choi } 725914b881fSChanwoo Choi subsys_initcall(sm5502_muic_i2c_init); 726914b881fSChanwoo Choi 727914b881fSChanwoo Choi MODULE_DESCRIPTION("Silicon Mitus SM5502 Extcon driver"); 728914b881fSChanwoo Choi MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>"); 729914b881fSChanwoo Choi MODULE_LICENSE("GPL"); 730