856 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * extcon-sm5502.c - Silicon Mitus SM5502 extcon drvier to support USB switches4 *5 * Copyright (c) 2014 Samsung Electronics Co., Ltd6 * Author: Chanwoo Choi <cw00.choi@samsung.com>7 */8 9#include <linux/err.h>10#include <linux/i2c.h>11#include <linux/interrupt.h>12#include <linux/irqdomain.h>13#include <linux/kernel.h>14#include <linux/module.h>15#include <linux/platform_device.h>16#include <linux/regmap.h>17#include <linux/slab.h>18#include <linux/extcon-provider.h>19 20#include "extcon-sm5502.h"21 22#define DELAY_MS_DEFAULT 17000 /* unit: millisecond */23 24struct muic_irq {25 unsigned int irq;26 const char *name;27 unsigned int virq;28};29 30struct reg_data {31 u8 reg;32 unsigned int val;33 bool invert;34};35 36struct sm5502_muic_info {37 struct device *dev;38 struct extcon_dev *edev;39 40 struct i2c_client *i2c;41 struct regmap *regmap;42 43 const struct sm5502_type *type;44 struct regmap_irq_chip_data *irq_data;45 int irq;46 bool irq_attach;47 bool irq_detach;48 struct work_struct irq_work;49 50 struct mutex mutex;51 52 /*53 * Use delayed workqueue to detect cable state and then54 * notify cable state to notifiee/platform through uevent.55 * After completing the booting of platform, the extcon provider56 * driver should notify cable state to upper layer.57 */58 struct delayed_work wq_detcable;59};60 61struct sm5502_type {62 struct muic_irq *muic_irqs;63 unsigned int num_muic_irqs;64 const struct regmap_irq_chip *irq_chip;65 66 struct reg_data *reg_data;67 unsigned int num_reg_data;68 69 unsigned int otg_dev_type1;70 int (*parse_irq)(struct sm5502_muic_info *info, int irq_type);71};72 73/* Default value of SM5502 register to bring up MUIC device. */74static struct reg_data sm5502_reg_data[] = {75 {76 .reg = SM5502_REG_RESET,77 .val = SM5502_REG_RESET_MASK,78 .invert = true,79 }, {80 .reg = SM5502_REG_CONTROL,81 .val = SM5502_REG_CONTROL_MASK_INT_MASK,82 .invert = false,83 }, {84 .reg = SM5502_REG_INTMASK1,85 .val = SM5502_REG_INTM1_KP_MASK86 | SM5502_REG_INTM1_LKP_MASK87 | SM5502_REG_INTM1_LKR_MASK,88 .invert = true,89 }, {90 .reg = SM5502_REG_INTMASK2,91 .val = SM5502_REG_INTM2_VBUS_DET_MASK92 | SM5502_REG_INTM2_REV_ACCE_MASK93 | SM5502_REG_INTM2_ADC_CHG_MASK94 | SM5502_REG_INTM2_STUCK_KEY_MASK95 | SM5502_REG_INTM2_STUCK_KEY_RCV_MASK96 | SM5502_REG_INTM2_MHL_MASK,97 .invert = true,98 },99};100 101/* Default value of SM5504 register to bring up MUIC device. */102static struct reg_data sm5504_reg_data[] = {103 {104 .reg = SM5502_REG_RESET,105 .val = SM5502_REG_RESET_MASK,106 .invert = true,107 }, {108 .reg = SM5502_REG_INTMASK1,109 .val = SM5504_REG_INTM1_ATTACH_MASK110 | SM5504_REG_INTM1_DETACH_MASK,111 .invert = false,112 }, {113 .reg = SM5502_REG_INTMASK2,114 .val = SM5504_REG_INTM2_RID_CHG_MASK115 | SM5504_REG_INTM2_UVLO_MASK116 | SM5504_REG_INTM2_POR_MASK,117 .invert = true,118 }, {119 .reg = SM5502_REG_CONTROL,120 .val = SM5502_REG_CONTROL_MANUAL_SW_MASK121 | SM5504_REG_CONTROL_CHGTYP_MASK122 | SM5504_REG_CONTROL_USBCHDEN_MASK123 | SM5504_REG_CONTROL_ADC_EN_MASK,124 .invert = true,125 },126};127 128/* List of detectable cables */129static const unsigned int sm5502_extcon_cable[] = {130 EXTCON_USB,131 EXTCON_USB_HOST,132 EXTCON_CHG_USB_SDP,133 EXTCON_CHG_USB_DCP,134 EXTCON_NONE,135};136 137/* Define supported accessory type */138enum sm5502_muic_acc_type {139 SM5502_MUIC_ADC_GROUND = 0x0,140 SM5502_MUIC_ADC_SEND_END_BUTTON,141 SM5502_MUIC_ADC_REMOTE_S1_BUTTON,142 SM5502_MUIC_ADC_REMOTE_S2_BUTTON,143 SM5502_MUIC_ADC_REMOTE_S3_BUTTON,144 SM5502_MUIC_ADC_REMOTE_S4_BUTTON,145 SM5502_MUIC_ADC_REMOTE_S5_BUTTON,146 SM5502_MUIC_ADC_REMOTE_S6_BUTTON,147 SM5502_MUIC_ADC_REMOTE_S7_BUTTON,148 SM5502_MUIC_ADC_REMOTE_S8_BUTTON,149 SM5502_MUIC_ADC_REMOTE_S9_BUTTON,150 SM5502_MUIC_ADC_REMOTE_S10_BUTTON,151 SM5502_MUIC_ADC_REMOTE_S11_BUTTON,152 SM5502_MUIC_ADC_REMOTE_S12_BUTTON,153 SM5502_MUIC_ADC_RESERVED_ACC_1,154 SM5502_MUIC_ADC_RESERVED_ACC_2,155 SM5502_MUIC_ADC_RESERVED_ACC_3,156 SM5502_MUIC_ADC_RESERVED_ACC_4,157 SM5502_MUIC_ADC_RESERVED_ACC_5,158 SM5502_MUIC_ADC_AUDIO_TYPE2,159 SM5502_MUIC_ADC_PHONE_POWERED_DEV,160 SM5502_MUIC_ADC_TTY_CONVERTER,161 SM5502_MUIC_ADC_UART_CABLE,162 SM5502_MUIC_ADC_TYPE1_CHARGER,163 SM5502_MUIC_ADC_FACTORY_MODE_BOOT_OFF_USB,164 SM5502_MUIC_ADC_FACTORY_MODE_BOOT_ON_USB,165 SM5502_MUIC_ADC_AUDIO_VIDEO_CABLE,166 SM5502_MUIC_ADC_TYPE2_CHARGER,167 SM5502_MUIC_ADC_FACTORY_MODE_BOOT_OFF_UART,168 SM5502_MUIC_ADC_FACTORY_MODE_BOOT_ON_UART,169 SM5502_MUIC_ADC_AUDIO_TYPE1,170 SM5502_MUIC_ADC_OPEN = 0x1f,171 172 /*173 * The below accessories have same ADC value (0x1f or 0x1e).174 * So, Device type1 is used to separate specific accessory.175 */176 /* |---------|--ADC| */177 /* | [7:5]|[4:0]| */178 SM5502_MUIC_ADC_AUDIO_TYPE1_FULL_REMOTE = 0x3e, /* | 001|11110| */179 SM5502_MUIC_ADC_AUDIO_TYPE1_SEND_END = 0x5e, /* | 010|11110| */180 /* |Dev Type1|--ADC| */181 SM5502_MUIC_ADC_GROUND_USB_OTG = 0x80, /* | 100|00000| */182 SM5502_MUIC_ADC_OPEN_USB = 0x5f, /* | 010|11111| */183 SM5502_MUIC_ADC_OPEN_TA = 0xdf, /* | 110|11111| */184 SM5502_MUIC_ADC_OPEN_USB_OTG = 0xff, /* | 111|11111| */185};186 187/* List of supported interrupt for SM5502 */188static struct muic_irq sm5502_muic_irqs[] = {189 { SM5502_IRQ_INT1_ATTACH, "muic-attach" },190 { SM5502_IRQ_INT1_DETACH, "muic-detach" },191 { SM5502_IRQ_INT1_KP, "muic-kp" },192 { SM5502_IRQ_INT1_LKP, "muic-lkp" },193 { SM5502_IRQ_INT1_LKR, "muic-lkr" },194 { SM5502_IRQ_INT1_OVP_EVENT, "muic-ovp-event" },195 { SM5502_IRQ_INT1_OCP_EVENT, "muic-ocp-event" },196 { SM5502_IRQ_INT1_OVP_OCP_DIS, "muic-ovp-ocp-dis" },197 { SM5502_IRQ_INT2_VBUS_DET, "muic-vbus-det" },198 { SM5502_IRQ_INT2_REV_ACCE, "muic-rev-acce" },199 { SM5502_IRQ_INT2_ADC_CHG, "muic-adc-chg" },200 { SM5502_IRQ_INT2_STUCK_KEY, "muic-stuck-key" },201 { SM5502_IRQ_INT2_STUCK_KEY_RCV, "muic-stuck-key-rcv" },202 { SM5502_IRQ_INT2_MHL, "muic-mhl" },203};204 205/* Define interrupt list of SM5502 to register regmap_irq */206static const struct regmap_irq sm5502_irqs[] = {207 /* INT1 interrupts */208 { .reg_offset = 0, .mask = SM5502_IRQ_INT1_ATTACH_MASK, },209 { .reg_offset = 0, .mask = SM5502_IRQ_INT1_DETACH_MASK, },210 { .reg_offset = 0, .mask = SM5502_IRQ_INT1_KP_MASK, },211 { .reg_offset = 0, .mask = SM5502_IRQ_INT1_LKP_MASK, },212 { .reg_offset = 0, .mask = SM5502_IRQ_INT1_LKR_MASK, },213 { .reg_offset = 0, .mask = SM5502_IRQ_INT1_OVP_EVENT_MASK, },214 { .reg_offset = 0, .mask = SM5502_IRQ_INT1_OCP_EVENT_MASK, },215 { .reg_offset = 0, .mask = SM5502_IRQ_INT1_OVP_OCP_DIS_MASK, },216 217 /* INT2 interrupts */218 { .reg_offset = 1, .mask = SM5502_IRQ_INT2_VBUS_DET_MASK,},219 { .reg_offset = 1, .mask = SM5502_IRQ_INT2_REV_ACCE_MASK, },220 { .reg_offset = 1, .mask = SM5502_IRQ_INT2_ADC_CHG_MASK, },221 { .reg_offset = 1, .mask = SM5502_IRQ_INT2_STUCK_KEY_MASK, },222 { .reg_offset = 1, .mask = SM5502_IRQ_INT2_STUCK_KEY_RCV_MASK, },223 { .reg_offset = 1, .mask = SM5502_IRQ_INT2_MHL_MASK, },224};225 226static const struct regmap_irq_chip sm5502_muic_irq_chip = {227 .name = "sm5502",228 .status_base = SM5502_REG_INT1,229 .mask_base = SM5502_REG_INTMASK1,230 .num_regs = 2,231 .irqs = sm5502_irqs,232 .num_irqs = ARRAY_SIZE(sm5502_irqs),233};234 235/* List of supported interrupt for SM5504 */236static struct muic_irq sm5504_muic_irqs[] = {237 { SM5504_IRQ_INT1_ATTACH, "muic-attach" },238 { SM5504_IRQ_INT1_DETACH, "muic-detach" },239 { SM5504_IRQ_INT1_CHG_DET, "muic-chg-det" },240 { SM5504_IRQ_INT1_DCD_OUT, "muic-dcd-out" },241 { SM5504_IRQ_INT1_OVP_EVENT, "muic-ovp-event" },242 { SM5504_IRQ_INT1_CONNECT, "muic-connect" },243 { SM5504_IRQ_INT1_ADC_CHG, "muic-adc-chg" },244 { SM5504_IRQ_INT2_RID_CHG, "muic-rid-chg" },245 { SM5504_IRQ_INT2_UVLO, "muic-uvlo" },246 { SM5504_IRQ_INT2_POR, "muic-por" },247 { SM5504_IRQ_INT2_OVP_FET, "muic-ovp-fet" },248 { SM5504_IRQ_INT2_OCP_LATCH, "muic-ocp-latch" },249 { SM5504_IRQ_INT2_OCP_EVENT, "muic-ocp-event" },250 { SM5504_IRQ_INT2_OVP_OCP_EVENT, "muic-ovp-ocp-event" },251};252 253/* Define interrupt list of SM5504 to register regmap_irq */254static const struct regmap_irq sm5504_irqs[] = {255 /* INT1 interrupts */256 { .reg_offset = 0, .mask = SM5504_IRQ_INT1_ATTACH_MASK, },257 { .reg_offset = 0, .mask = SM5504_IRQ_INT1_DETACH_MASK, },258 { .reg_offset = 0, .mask = SM5504_IRQ_INT1_CHG_DET_MASK, },259 { .reg_offset = 0, .mask = SM5504_IRQ_INT1_DCD_OUT_MASK, },260 { .reg_offset = 0, .mask = SM5504_IRQ_INT1_OVP_MASK, },261 { .reg_offset = 0, .mask = SM5504_IRQ_INT1_CONNECT_MASK, },262 { .reg_offset = 0, .mask = SM5504_IRQ_INT1_ADC_CHG_MASK, },263 264 /* INT2 interrupts */265 { .reg_offset = 1, .mask = SM5504_IRQ_INT2_RID_CHG_MASK,},266 { .reg_offset = 1, .mask = SM5504_IRQ_INT2_UVLO_MASK, },267 { .reg_offset = 1, .mask = SM5504_IRQ_INT2_POR_MASK, },268 { .reg_offset = 1, .mask = SM5504_IRQ_INT2_OVP_FET_MASK, },269 { .reg_offset = 1, .mask = SM5504_IRQ_INT2_OCP_LATCH_MASK, },270 { .reg_offset = 1, .mask = SM5504_IRQ_INT2_OCP_EVENT_MASK, },271 { .reg_offset = 1, .mask = SM5504_IRQ_INT2_OVP_OCP_EVENT_MASK, },272};273 274static const struct regmap_irq_chip sm5504_muic_irq_chip = {275 .name = "sm5504",276 .status_base = SM5502_REG_INT1,277 .mask_base = SM5502_REG_INTMASK1,278 .num_regs = 2,279 .irqs = sm5504_irqs,280 .num_irqs = ARRAY_SIZE(sm5504_irqs),281};282 283/* Define regmap configuration of SM5502 for I2C communication */284static bool sm5502_muic_volatile_reg(struct device *dev, unsigned int reg)285{286 switch (reg) {287 case SM5502_REG_INTMASK1:288 case SM5502_REG_INTMASK2:289 return true;290 default:291 break;292 }293 return false;294}295 296static const struct regmap_config sm5502_muic_regmap_config = {297 .reg_bits = 8,298 .val_bits = 8,299 .volatile_reg = sm5502_muic_volatile_reg,300 .max_register = SM5502_REG_END,301};302 303/* Change DM_CON/DP_CON/VBUSIN switch according to cable type */304static int sm5502_muic_set_path(struct sm5502_muic_info *info,305 unsigned int con_sw, unsigned int vbus_sw,306 bool attached)307{308 int ret;309 310 if (!attached) {311 con_sw = DM_DP_SWITCH_OPEN;312 vbus_sw = VBUSIN_SWITCH_OPEN;313 }314 315 switch (con_sw) {316 case DM_DP_SWITCH_OPEN:317 case DM_DP_SWITCH_USB:318 case DM_DP_SWITCH_AUDIO:319 case DM_DP_SWITCH_UART:320 ret = regmap_update_bits(info->regmap, SM5502_REG_MANUAL_SW1,321 SM5502_REG_MANUAL_SW1_DP_MASK |322 SM5502_REG_MANUAL_SW1_DM_MASK,323 con_sw);324 if (ret < 0) {325 dev_err(info->dev,326 "cannot update DM_CON/DP_CON switch\n");327 return ret;328 }329 break;330 default:331 dev_err(info->dev, "Unknown DM_CON/DP_CON switch type (%d)\n",332 con_sw);333 return -EINVAL;334 }335 336 switch (vbus_sw) {337 case VBUSIN_SWITCH_OPEN:338 case VBUSIN_SWITCH_VBUSOUT:339 case VBUSIN_SWITCH_MIC:340 case VBUSIN_SWITCH_VBUSOUT_WITH_USB:341 ret = regmap_update_bits(info->regmap, SM5502_REG_MANUAL_SW1,342 SM5502_REG_MANUAL_SW1_VBUSIN_MASK,343 vbus_sw);344 if (ret < 0) {345 dev_err(info->dev,346 "cannot update VBUSIN switch\n");347 return ret;348 }349 break;350 default:351 dev_err(info->dev, "Unknown VBUS switch type (%d)\n", vbus_sw);352 return -EINVAL;353 }354 355 return 0;356}357 358/* Return cable type of attached or detached accessories */359static unsigned int sm5502_muic_get_cable_type(struct sm5502_muic_info *info)360{361 unsigned int cable_type, adc, dev_type1;362 int ret;363 364 /* Read ADC value according to external cable or button */365 ret = regmap_read(info->regmap, SM5502_REG_ADC, &adc);366 if (ret) {367 dev_err(info->dev, "failed to read ADC register\n");368 return ret;369 }370 371 /*372 * If ADC is SM5502_MUIC_ADC_GROUND(0x0), external cable hasn't373 * connected with to MUIC device.374 */375 cable_type = adc & SM5502_REG_ADC_MASK;376 377 switch (cable_type) {378 case SM5502_MUIC_ADC_GROUND:379 ret = regmap_read(info->regmap, SM5502_REG_DEV_TYPE1,380 &dev_type1);381 if (ret) {382 dev_err(info->dev, "failed to read DEV_TYPE1 reg\n");383 return ret;384 }385 386 if (dev_type1 == info->type->otg_dev_type1) {387 cable_type = SM5502_MUIC_ADC_GROUND_USB_OTG;388 } else {389 dev_dbg(info->dev,390 "cannot identify the cable type: adc(0x%x), dev_type1(0x%x)\n",391 adc, dev_type1);392 return -EINVAL;393 }394 break;395 case SM5502_MUIC_ADC_SEND_END_BUTTON:396 case SM5502_MUIC_ADC_REMOTE_S1_BUTTON:397 case SM5502_MUIC_ADC_REMOTE_S2_BUTTON:398 case SM5502_MUIC_ADC_REMOTE_S3_BUTTON:399 case SM5502_MUIC_ADC_REMOTE_S4_BUTTON:400 case SM5502_MUIC_ADC_REMOTE_S5_BUTTON:401 case SM5502_MUIC_ADC_REMOTE_S6_BUTTON:402 case SM5502_MUIC_ADC_REMOTE_S7_BUTTON:403 case SM5502_MUIC_ADC_REMOTE_S8_BUTTON:404 case SM5502_MUIC_ADC_REMOTE_S9_BUTTON:405 case SM5502_MUIC_ADC_REMOTE_S10_BUTTON:406 case SM5502_MUIC_ADC_REMOTE_S11_BUTTON:407 case SM5502_MUIC_ADC_REMOTE_S12_BUTTON:408 case SM5502_MUIC_ADC_RESERVED_ACC_1:409 case SM5502_MUIC_ADC_RESERVED_ACC_2:410 case SM5502_MUIC_ADC_RESERVED_ACC_3:411 case SM5502_MUIC_ADC_RESERVED_ACC_4:412 case SM5502_MUIC_ADC_RESERVED_ACC_5:413 case SM5502_MUIC_ADC_AUDIO_TYPE2:414 case SM5502_MUIC_ADC_PHONE_POWERED_DEV:415 case SM5502_MUIC_ADC_TTY_CONVERTER:416 case SM5502_MUIC_ADC_UART_CABLE:417 case SM5502_MUIC_ADC_TYPE1_CHARGER:418 case SM5502_MUIC_ADC_FACTORY_MODE_BOOT_OFF_USB:419 case SM5502_MUIC_ADC_FACTORY_MODE_BOOT_ON_USB:420 case SM5502_MUIC_ADC_AUDIO_VIDEO_CABLE:421 case SM5502_MUIC_ADC_TYPE2_CHARGER:422 case SM5502_MUIC_ADC_FACTORY_MODE_BOOT_OFF_UART:423 case SM5502_MUIC_ADC_FACTORY_MODE_BOOT_ON_UART:424 break;425 case SM5502_MUIC_ADC_AUDIO_TYPE1:426 /*427 * Check whether cable type is428 * SM5502_MUIC_ADC_AUDIO_TYPE1_FULL_REMOTE429 * or SM5502_MUIC_ADC_AUDIO_TYPE1_SEND_END430 * by using Button event.431 */432 break;433 case SM5502_MUIC_ADC_OPEN:434 ret = regmap_read(info->regmap, SM5502_REG_DEV_TYPE1,435 &dev_type1);436 if (ret) {437 dev_err(info->dev, "failed to read DEV_TYPE1 reg\n");438 return ret;439 }440 441 if (dev_type1 == info->type->otg_dev_type1) {442 cable_type = SM5502_MUIC_ADC_OPEN_USB_OTG;443 break;444 }445 446 switch (dev_type1) {447 case SM5502_REG_DEV_TYPE1_USB_SDP_MASK:448 cable_type = SM5502_MUIC_ADC_OPEN_USB;449 break;450 case SM5502_REG_DEV_TYPE1_DEDICATED_CHG_MASK:451 cable_type = SM5502_MUIC_ADC_OPEN_TA;452 break;453 default:454 dev_dbg(info->dev,455 "cannot identify the cable type: adc(0x%x)\n",456 adc);457 return -EINVAL;458 }459 break;460 default:461 dev_err(info->dev,462 "failed to identify the cable type: adc(0x%x)\n", adc);463 return -EINVAL;464 }465 466 return cable_type;467}468 469static int sm5502_muic_cable_handler(struct sm5502_muic_info *info,470 bool attached)471{472 static unsigned int prev_cable_type = SM5502_MUIC_ADC_GROUND;473 unsigned int cable_type = SM5502_MUIC_ADC_GROUND;474 unsigned int con_sw = DM_DP_SWITCH_OPEN;475 unsigned int vbus_sw = VBUSIN_SWITCH_OPEN;476 unsigned int id;477 int ret;478 479 /* Get the type of attached or detached cable */480 if (attached)481 cable_type = sm5502_muic_get_cable_type(info);482 else483 cable_type = prev_cable_type;484 prev_cable_type = cable_type;485 486 switch (cable_type) {487 case SM5502_MUIC_ADC_OPEN_USB:488 id = EXTCON_USB;489 con_sw = DM_DP_SWITCH_USB;490 vbus_sw = VBUSIN_SWITCH_VBUSOUT_WITH_USB;491 break;492 case SM5502_MUIC_ADC_OPEN_TA:493 id = EXTCON_CHG_USB_DCP;494 con_sw = DM_DP_SWITCH_OPEN;495 vbus_sw = VBUSIN_SWITCH_VBUSOUT;496 break;497 case SM5502_MUIC_ADC_GROUND_USB_OTG:498 case SM5502_MUIC_ADC_OPEN_USB_OTG:499 id = EXTCON_USB_HOST;500 con_sw = DM_DP_SWITCH_USB;501 vbus_sw = VBUSIN_SWITCH_OPEN;502 break;503 default:504 dev_dbg(info->dev,505 "cannot handle this cable_type (0x%x)\n", cable_type);506 return 0;507 }508 509 /* Change internal hardware path(DM_CON/DP_CON, VBUSIN) */510 ret = sm5502_muic_set_path(info, con_sw, vbus_sw, attached);511 if (ret < 0)512 return ret;513 514 /* Change the state of external accessory */515 extcon_set_state_sync(info->edev, id, attached);516 if (id == EXTCON_USB)517 extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SDP,518 attached);519 520 return 0;521}522 523static void sm5502_muic_irq_work(struct work_struct *work)524{525 struct sm5502_muic_info *info = container_of(work,526 struct sm5502_muic_info, irq_work);527 int ret = 0;528 529 if (!info->edev)530 return;531 532 mutex_lock(&info->mutex);533 534 /* Detect attached or detached cables */535 if (info->irq_attach) {536 ret = sm5502_muic_cable_handler(info, true);537 info->irq_attach = false;538 }539 if (info->irq_detach) {540 ret = sm5502_muic_cable_handler(info, false);541 info->irq_detach = false;542 }543 544 if (ret < 0)545 dev_err(info->dev, "failed to handle MUIC interrupt\n");546 547 mutex_unlock(&info->mutex);548}549 550/*551 * Sets irq_attach or irq_detach in sm5502_muic_info and returns 0.552 * Returns -ESRCH if irq_type does not match registered IRQ for this dev type.553 */554static int sm5502_parse_irq(struct sm5502_muic_info *info, int irq_type)555{556 switch (irq_type) {557 case SM5502_IRQ_INT1_ATTACH:558 info->irq_attach = true;559 break;560 case SM5502_IRQ_INT1_DETACH:561 info->irq_detach = true;562 break;563 case SM5502_IRQ_INT1_KP:564 case SM5502_IRQ_INT1_LKP:565 case SM5502_IRQ_INT1_LKR:566 case SM5502_IRQ_INT1_OVP_EVENT:567 case SM5502_IRQ_INT1_OCP_EVENT:568 case SM5502_IRQ_INT1_OVP_OCP_DIS:569 case SM5502_IRQ_INT2_VBUS_DET:570 case SM5502_IRQ_INT2_REV_ACCE:571 case SM5502_IRQ_INT2_ADC_CHG:572 case SM5502_IRQ_INT2_STUCK_KEY:573 case SM5502_IRQ_INT2_STUCK_KEY_RCV:574 case SM5502_IRQ_INT2_MHL:575 default:576 break;577 }578 579 return 0;580}581 582static int sm5504_parse_irq(struct sm5502_muic_info *info, int irq_type)583{584 switch (irq_type) {585 case SM5504_IRQ_INT1_ATTACH:586 info->irq_attach = true;587 break;588 case SM5504_IRQ_INT1_DETACH:589 info->irq_detach = true;590 break;591 case SM5504_IRQ_INT1_CHG_DET:592 case SM5504_IRQ_INT1_DCD_OUT:593 case SM5504_IRQ_INT1_OVP_EVENT:594 case SM5504_IRQ_INT1_CONNECT:595 case SM5504_IRQ_INT1_ADC_CHG:596 case SM5504_IRQ_INT2_RID_CHG:597 case SM5504_IRQ_INT2_UVLO:598 case SM5504_IRQ_INT2_POR:599 case SM5504_IRQ_INT2_OVP_FET:600 case SM5504_IRQ_INT2_OCP_LATCH:601 case SM5504_IRQ_INT2_OCP_EVENT:602 case SM5504_IRQ_INT2_OVP_OCP_EVENT:603 default:604 break;605 }606 607 return 0;608}609 610static irqreturn_t sm5502_muic_irq_handler(int irq, void *data)611{612 struct sm5502_muic_info *info = data;613 int i, irq_type = -1, ret;614 615 for (i = 0; i < info->type->num_muic_irqs; i++)616 if (irq == info->type->muic_irqs[i].virq)617 irq_type = info->type->muic_irqs[i].irq;618 619 ret = info->type->parse_irq(info, irq_type);620 if (ret < 0) {621 dev_warn(info->dev, "cannot handle is interrupt:%d\n",622 irq_type);623 return IRQ_HANDLED;624 }625 schedule_work(&info->irq_work);626 627 return IRQ_HANDLED;628}629 630static void sm5502_muic_detect_cable_wq(struct work_struct *work)631{632 struct sm5502_muic_info *info = container_of(to_delayed_work(work),633 struct sm5502_muic_info, wq_detcable);634 int ret;635 636 /* Notify the state of connector cable or not */637 ret = sm5502_muic_cable_handler(info, true);638 if (ret < 0)639 dev_warn(info->dev, "failed to detect cable state\n");640}641 642static void sm5502_init_dev_type(struct sm5502_muic_info *info)643{644 unsigned int reg_data, vendor_id, version_id;645 int i, ret;646 647 /* To test I2C, Print version_id and vendor_id of SM5502 */648 ret = regmap_read(info->regmap, SM5502_REG_DEVICE_ID, ®_data);649 if (ret) {650 dev_err(info->dev,651 "failed to read DEVICE_ID register: %d\n", ret);652 return;653 }654 655 vendor_id = ((reg_data & SM5502_REG_DEVICE_ID_VENDOR_MASK) >>656 SM5502_REG_DEVICE_ID_VENDOR_SHIFT);657 version_id = ((reg_data & SM5502_REG_DEVICE_ID_VERSION_MASK) >>658 SM5502_REG_DEVICE_ID_VERSION_SHIFT);659 660 dev_info(info->dev, "Device type: version: 0x%x, vendor: 0x%x\n",661 version_id, vendor_id);662 663 /* Initiazle the register of SM5502 device to bring-up */664 for (i = 0; i < info->type->num_reg_data; i++) {665 unsigned int val = 0;666 667 if (!info->type->reg_data[i].invert)668 val |= ~info->type->reg_data[i].val;669 else670 val = info->type->reg_data[i].val;671 regmap_write(info->regmap, info->type->reg_data[i].reg, val);672 }673}674 675static int sm5022_muic_i2c_probe(struct i2c_client *i2c)676{677 struct device_node *np = i2c->dev.of_node;678 struct sm5502_muic_info *info;679 int i, ret, irq_flags;680 681 if (!np)682 return -EINVAL;683 684 info = devm_kzalloc(&i2c->dev, sizeof(*info), GFP_KERNEL);685 if (!info)686 return -ENOMEM;687 i2c_set_clientdata(i2c, info);688 689 info->dev = &i2c->dev;690 info->i2c = i2c;691 info->irq = i2c->irq;692 info->type = device_get_match_data(info->dev);693 if (!info->type)694 return -EINVAL;695 if (!info->type->parse_irq) {696 dev_err(info->dev, "parse_irq missing in struct sm5502_type\n");697 return -EINVAL;698 }699 700 mutex_init(&info->mutex);701 702 INIT_WORK(&info->irq_work, sm5502_muic_irq_work);703 704 info->regmap = devm_regmap_init_i2c(i2c, &sm5502_muic_regmap_config);705 if (IS_ERR(info->regmap)) {706 ret = PTR_ERR(info->regmap);707 dev_err(info->dev, "failed to allocate register map: %d\n",708 ret);709 return ret;710 }711 712 /* Support irq domain for SM5502 MUIC device */713 irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT | IRQF_SHARED;714 ret = devm_regmap_add_irq_chip(info->dev, info->regmap, info->irq,715 irq_flags, 0, info->type->irq_chip,716 &info->irq_data);717 if (ret != 0) {718 dev_err(info->dev, "failed to request IRQ %d: %d\n",719 info->irq, ret);720 return ret;721 }722 723 for (i = 0; i < info->type->num_muic_irqs; i++) {724 struct muic_irq *muic_irq = &info->type->muic_irqs[i];725 int virq = 0;726 727 virq = regmap_irq_get_virq(info->irq_data, muic_irq->irq);728 if (virq <= 0)729 return -EINVAL;730 muic_irq->virq = virq;731 732 ret = devm_request_threaded_irq(info->dev, virq, NULL,733 sm5502_muic_irq_handler,734 IRQF_NO_SUSPEND | IRQF_ONESHOT,735 muic_irq->name, info);736 if (ret) {737 dev_err(info->dev,738 "failed: irq request (IRQ: %d, error :%d)\n",739 muic_irq->irq, ret);740 return ret;741 }742 }743 744 /* Allocate extcon device */745 info->edev = devm_extcon_dev_allocate(info->dev, sm5502_extcon_cable);746 if (IS_ERR(info->edev)) {747 dev_err(info->dev, "failed to allocate memory for extcon\n");748 return -ENOMEM;749 }750 751 /* Register extcon device */752 ret = devm_extcon_dev_register(info->dev, info->edev);753 if (ret) {754 dev_err(info->dev, "failed to register extcon device\n");755 return ret;756 }757 758 /*759 * Detect accessory after completing the initialization of platform760 *761 * - Use delayed workqueue to detect cable state and then762 * notify cable state to notifiee/platform through uevent.763 * After completing the booting of platform, the extcon provider764 * driver should notify cable state to upper layer.765 */766 INIT_DELAYED_WORK(&info->wq_detcable, sm5502_muic_detect_cable_wq);767 queue_delayed_work(system_power_efficient_wq, &info->wq_detcable,768 msecs_to_jiffies(DELAY_MS_DEFAULT));769 770 /* Initialize SM5502 device and print vendor id and version id */771 sm5502_init_dev_type(info);772 773 return 0;774}775 776static const struct sm5502_type sm5502_data = {777 .muic_irqs = sm5502_muic_irqs,778 .num_muic_irqs = ARRAY_SIZE(sm5502_muic_irqs),779 .irq_chip = &sm5502_muic_irq_chip,780 .reg_data = sm5502_reg_data,781 .num_reg_data = ARRAY_SIZE(sm5502_reg_data),782 .otg_dev_type1 = SM5502_REG_DEV_TYPE1_USB_OTG_MASK,783 .parse_irq = sm5502_parse_irq,784};785 786static const struct sm5502_type sm5504_data = {787 .muic_irqs = sm5504_muic_irqs,788 .num_muic_irqs = ARRAY_SIZE(sm5504_muic_irqs),789 .irq_chip = &sm5504_muic_irq_chip,790 .reg_data = sm5504_reg_data,791 .num_reg_data = ARRAY_SIZE(sm5504_reg_data),792 .otg_dev_type1 = SM5504_REG_DEV_TYPE1_USB_OTG_MASK,793 .parse_irq = sm5504_parse_irq,794};795 796static const struct of_device_id sm5502_dt_match[] = {797 { .compatible = "siliconmitus,sm5502-muic", .data = &sm5502_data },798 { .compatible = "siliconmitus,sm5504-muic", .data = &sm5504_data },799 { .compatible = "siliconmitus,sm5703-muic", .data = &sm5502_data },800 { },801};802MODULE_DEVICE_TABLE(of, sm5502_dt_match);803 804#ifdef CONFIG_PM_SLEEP805static int sm5502_muic_suspend(struct device *dev)806{807 struct i2c_client *i2c = to_i2c_client(dev);808 struct sm5502_muic_info *info = i2c_get_clientdata(i2c);809 810 enable_irq_wake(info->irq);811 812 return 0;813}814 815static int sm5502_muic_resume(struct device *dev)816{817 struct i2c_client *i2c = to_i2c_client(dev);818 struct sm5502_muic_info *info = i2c_get_clientdata(i2c);819 820 disable_irq_wake(info->irq);821 822 return 0;823}824#endif825 826static SIMPLE_DEV_PM_OPS(sm5502_muic_pm_ops,827 sm5502_muic_suspend, sm5502_muic_resume);828 829static const struct i2c_device_id sm5502_i2c_id[] = {830 { "sm5502", (kernel_ulong_t)&sm5502_data },831 { "sm5504", (kernel_ulong_t)&sm5504_data },832 { "sm5703-muic", (kernel_ulong_t)&sm5502_data },833 { }834};835MODULE_DEVICE_TABLE(i2c, sm5502_i2c_id);836 837static struct i2c_driver sm5502_muic_i2c_driver = {838 .driver = {839 .name = "sm5502",840 .pm = &sm5502_muic_pm_ops,841 .of_match_table = sm5502_dt_match,842 },843 .probe = sm5022_muic_i2c_probe,844 .id_table = sm5502_i2c_id,845};846 847static int __init sm5502_muic_i2c_init(void)848{849 return i2c_add_driver(&sm5502_muic_i2c_driver);850}851subsys_initcall(sm5502_muic_i2c_init);852 853MODULE_DESCRIPTION("Silicon Mitus SM5502 Extcon driver");854MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>");855MODULE_LICENSE("GPL");856