linux/drivers/extcon/extcon-sm5502.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * extcon-sm5502.c - Silicon Mitus SM5502 extcon drvier to support USB switches
   4 *
   5 * Copyright (c) 2014 Samsung Electronics Co., Ltd
   6 * 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 then
  54         * notify cable state to notifiee/platform through uevent.
  55         * After completing the booting of platform, the extcon provider
  56         * 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_MASK
  86                        | SM5502_REG_INTM1_LKP_MASK
  87                        | SM5502_REG_INTM1_LKR_MASK,
  88                .invert = true,
  89        }, {
  90                .reg = SM5502_REG_INTMASK2,
  91                .val = SM5502_REG_INTM2_VBUS_DET_MASK
  92                        | SM5502_REG_INTM2_REV_ACCE_MASK
  93                        | SM5502_REG_INTM2_ADC_CHG_MASK
  94                        | SM5502_REG_INTM2_STUCK_KEY_MASK
  95                        | SM5502_REG_INTM2_STUCK_KEY_RCV_MASK
  96                        | 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_MASK
 110                        | SM5504_REG_INTM1_DETACH_MASK,
 111                .invert = false,
 112        }, {
 113                .reg = SM5502_REG_INTMASK2,
 114                .val = SM5504_REG_INTM2_RID_CHG_MASK
 115                        | SM5504_REG_INTM2_UVLO_MASK
 116                        | SM5504_REG_INTM2_POR_MASK,
 117                .invert = true,
 118        }, {
 119                .reg = SM5502_REG_CONTROL,
 120                .val = SM5502_REG_CONTROL_MANUAL_SW_MASK
 121                        | SM5504_REG_CONTROL_CHGTYP_MASK
 122                        | SM5504_REG_CONTROL_USBCHDEN_MASK
 123                        | 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        .mask_invert            = false,
 231        .num_regs               = 2,
 232        .irqs                   = sm5502_irqs,
 233        .num_irqs               = ARRAY_SIZE(sm5502_irqs),
 234};
 235
 236/* List of supported interrupt for SM5504 */
 237static struct muic_irq sm5504_muic_irqs[] = {
 238        { SM5504_IRQ_INT1_ATTACH,       "muic-attach" },
 239        { SM5504_IRQ_INT1_DETACH,       "muic-detach" },
 240        { SM5504_IRQ_INT1_CHG_DET,      "muic-chg-det" },
 241        { SM5504_IRQ_INT1_DCD_OUT,      "muic-dcd-out" },
 242        { SM5504_IRQ_INT1_OVP_EVENT,    "muic-ovp-event" },
 243        { SM5504_IRQ_INT1_CONNECT,      "muic-connect" },
 244        { SM5504_IRQ_INT1_ADC_CHG,      "muic-adc-chg" },
 245        { SM5504_IRQ_INT2_RID_CHG,      "muic-rid-chg" },
 246        { SM5504_IRQ_INT2_UVLO,         "muic-uvlo" },
 247        { SM5504_IRQ_INT2_POR,          "muic-por" },
 248        { SM5504_IRQ_INT2_OVP_FET,      "muic-ovp-fet" },
 249        { SM5504_IRQ_INT2_OCP_LATCH,    "muic-ocp-latch" },
 250        { SM5504_IRQ_INT2_OCP_EVENT,    "muic-ocp-event" },
 251        { SM5504_IRQ_INT2_OVP_OCP_EVENT, "muic-ovp-ocp-event" },
 252};
 253
 254/* Define interrupt list of SM5504 to register regmap_irq */
 255static const struct regmap_irq sm5504_irqs[] = {
 256        /* INT1 interrupts */
 257        { .reg_offset = 0, .mask = SM5504_IRQ_INT1_ATTACH_MASK, },
 258        { .reg_offset = 0, .mask = SM5504_IRQ_INT1_DETACH_MASK, },
 259        { .reg_offset = 0, .mask = SM5504_IRQ_INT1_CHG_DET_MASK, },
 260        { .reg_offset = 0, .mask = SM5504_IRQ_INT1_DCD_OUT_MASK, },
 261        { .reg_offset = 0, .mask = SM5504_IRQ_INT1_OVP_MASK, },
 262        { .reg_offset = 0, .mask = SM5504_IRQ_INT1_CONNECT_MASK, },
 263        { .reg_offset = 0, .mask = SM5504_IRQ_INT1_ADC_CHG_MASK, },
 264
 265        /* INT2 interrupts */
 266        { .reg_offset = 1, .mask = SM5504_IRQ_INT2_RID_CHG_MASK,},
 267        { .reg_offset = 1, .mask = SM5504_IRQ_INT2_UVLO_MASK, },
 268        { .reg_offset = 1, .mask = SM5504_IRQ_INT2_POR_MASK, },
 269        { .reg_offset = 1, .mask = SM5504_IRQ_INT2_OVP_FET_MASK, },
 270        { .reg_offset = 1, .mask = SM5504_IRQ_INT2_OCP_LATCH_MASK, },
 271        { .reg_offset = 1, .mask = SM5504_IRQ_INT2_OCP_EVENT_MASK, },
 272        { .reg_offset = 1, .mask = SM5504_IRQ_INT2_OVP_OCP_EVENT_MASK, },
 273};
 274
 275static const struct regmap_irq_chip sm5504_muic_irq_chip = {
 276        .name                   = "sm5504",
 277        .status_base            = SM5502_REG_INT1,
 278        .mask_base              = SM5502_REG_INTMASK1,
 279        .mask_invert            = false,
 280        .num_regs               = 2,
 281        .irqs                   = sm5504_irqs,
 282        .num_irqs               = ARRAY_SIZE(sm5504_irqs),
 283};
 284
 285/* Define regmap configuration of SM5502 for I2C communication  */
 286static bool sm5502_muic_volatile_reg(struct device *dev, unsigned int reg)
 287{
 288        switch (reg) {
 289        case SM5502_REG_INTMASK1:
 290        case SM5502_REG_INTMASK2:
 291                return true;
 292        default:
 293                break;
 294        }
 295        return false;
 296}
 297
 298static const struct regmap_config sm5502_muic_regmap_config = {
 299        .reg_bits       = 8,
 300        .val_bits       = 8,
 301        .volatile_reg   = sm5502_muic_volatile_reg,
 302        .max_register   = SM5502_REG_END,
 303};
 304
 305/* Change DM_CON/DP_CON/VBUSIN switch according to cable type */
 306static int sm5502_muic_set_path(struct sm5502_muic_info *info,
 307                                unsigned int con_sw, unsigned int vbus_sw,
 308                                bool attached)
 309{
 310        int ret;
 311
 312        if (!attached) {
 313                con_sw  = DM_DP_SWITCH_OPEN;
 314                vbus_sw = VBUSIN_SWITCH_OPEN;
 315        }
 316
 317        switch (con_sw) {
 318        case DM_DP_SWITCH_OPEN:
 319        case DM_DP_SWITCH_USB:
 320        case DM_DP_SWITCH_AUDIO:
 321        case DM_DP_SWITCH_UART:
 322                ret = regmap_update_bits(info->regmap, SM5502_REG_MANUAL_SW1,
 323                                         SM5502_REG_MANUAL_SW1_DP_MASK |
 324                                         SM5502_REG_MANUAL_SW1_DM_MASK,
 325                                         con_sw);
 326                if (ret < 0) {
 327                        dev_err(info->dev,
 328                                "cannot update DM_CON/DP_CON switch\n");
 329                        return ret;
 330                }
 331                break;
 332        default:
 333                dev_err(info->dev, "Unknown DM_CON/DP_CON switch type (%d)\n",
 334                                con_sw);
 335                return -EINVAL;
 336        }
 337
 338        switch (vbus_sw) {
 339        case VBUSIN_SWITCH_OPEN:
 340        case VBUSIN_SWITCH_VBUSOUT:
 341        case VBUSIN_SWITCH_MIC:
 342        case VBUSIN_SWITCH_VBUSOUT_WITH_USB:
 343                ret = regmap_update_bits(info->regmap, SM5502_REG_MANUAL_SW1,
 344                                         SM5502_REG_MANUAL_SW1_VBUSIN_MASK,
 345                                         vbus_sw);
 346                if (ret < 0) {
 347                        dev_err(info->dev,
 348                                "cannot update VBUSIN switch\n");
 349                        return ret;
 350                }
 351                break;
 352        default:
 353                dev_err(info->dev, "Unknown VBUS switch type (%d)\n", vbus_sw);
 354                return -EINVAL;
 355        }
 356
 357        return 0;
 358}
 359
 360/* Return cable type of attached or detached accessories */
 361static unsigned int sm5502_muic_get_cable_type(struct sm5502_muic_info *info)
 362{
 363        unsigned int cable_type, adc, dev_type1;
 364        int ret;
 365
 366        /* Read ADC value according to external cable or button */
 367        ret = regmap_read(info->regmap, SM5502_REG_ADC, &adc);
 368        if (ret) {
 369                dev_err(info->dev, "failed to read ADC register\n");
 370                return ret;
 371        }
 372
 373        /*
 374         * If ADC is SM5502_MUIC_ADC_GROUND(0x0), external cable hasn't
 375         * connected with to MUIC device.
 376         */
 377        cable_type = adc & SM5502_REG_ADC_MASK;
 378
 379        switch (cable_type) {
 380        case SM5502_MUIC_ADC_GROUND:
 381                ret = regmap_read(info->regmap, SM5502_REG_DEV_TYPE1,
 382                                  &dev_type1);
 383                if (ret) {
 384                        dev_err(info->dev, "failed to read DEV_TYPE1 reg\n");
 385                        return ret;
 386                }
 387
 388                if (dev_type1 == info->type->otg_dev_type1) {
 389                        cable_type = SM5502_MUIC_ADC_GROUND_USB_OTG;
 390                } else {
 391                        dev_dbg(info->dev,
 392                                "cannot identify the cable type: adc(0x%x), dev_type1(0x%x)\n",
 393                                adc, dev_type1);
 394                        return -EINVAL;
 395                }
 396                break;
 397        case SM5502_MUIC_ADC_SEND_END_BUTTON:
 398        case SM5502_MUIC_ADC_REMOTE_S1_BUTTON:
 399        case SM5502_MUIC_ADC_REMOTE_S2_BUTTON:
 400        case SM5502_MUIC_ADC_REMOTE_S3_BUTTON:
 401        case SM5502_MUIC_ADC_REMOTE_S4_BUTTON:
 402        case SM5502_MUIC_ADC_REMOTE_S5_BUTTON:
 403        case SM5502_MUIC_ADC_REMOTE_S6_BUTTON:
 404        case SM5502_MUIC_ADC_REMOTE_S7_BUTTON:
 405        case SM5502_MUIC_ADC_REMOTE_S8_BUTTON:
 406        case SM5502_MUIC_ADC_REMOTE_S9_BUTTON:
 407        case SM5502_MUIC_ADC_REMOTE_S10_BUTTON:
 408        case SM5502_MUIC_ADC_REMOTE_S11_BUTTON:
 409        case SM5502_MUIC_ADC_REMOTE_S12_BUTTON:
 410        case SM5502_MUIC_ADC_RESERVED_ACC_1:
 411        case SM5502_MUIC_ADC_RESERVED_ACC_2:
 412        case SM5502_MUIC_ADC_RESERVED_ACC_3:
 413        case SM5502_MUIC_ADC_RESERVED_ACC_4:
 414        case SM5502_MUIC_ADC_RESERVED_ACC_5:
 415        case SM5502_MUIC_ADC_AUDIO_TYPE2:
 416        case SM5502_MUIC_ADC_PHONE_POWERED_DEV:
 417        case SM5502_MUIC_ADC_TTY_CONVERTER:
 418        case SM5502_MUIC_ADC_UART_CABLE:
 419        case SM5502_MUIC_ADC_TYPE1_CHARGER:
 420        case SM5502_MUIC_ADC_FACTORY_MODE_BOOT_OFF_USB:
 421        case SM5502_MUIC_ADC_FACTORY_MODE_BOOT_ON_USB:
 422        case SM5502_MUIC_ADC_AUDIO_VIDEO_CABLE:
 423        case SM5502_MUIC_ADC_TYPE2_CHARGER:
 424        case SM5502_MUIC_ADC_FACTORY_MODE_BOOT_OFF_UART:
 425        case SM5502_MUIC_ADC_FACTORY_MODE_BOOT_ON_UART:
 426                break;
 427        case SM5502_MUIC_ADC_AUDIO_TYPE1:
 428                /*
 429                 * Check whether cable type is
 430                 * SM5502_MUIC_ADC_AUDIO_TYPE1_FULL_REMOTE
 431                 * or SM5502_MUIC_ADC_AUDIO_TYPE1_SEND_END
 432                 * by using Button event.
 433                 */
 434                break;
 435        case SM5502_MUIC_ADC_OPEN:
 436                ret = regmap_read(info->regmap, SM5502_REG_DEV_TYPE1,
 437                                  &dev_type1);
 438                if (ret) {
 439                        dev_err(info->dev, "failed to read DEV_TYPE1 reg\n");
 440                        return ret;
 441                }
 442
 443                if (dev_type1 == info->type->otg_dev_type1) {
 444                        cable_type = SM5502_MUIC_ADC_OPEN_USB_OTG;
 445                        break;
 446                }
 447
 448                switch (dev_type1) {
 449                case SM5502_REG_DEV_TYPE1_USB_SDP_MASK:
 450                        cable_type = SM5502_MUIC_ADC_OPEN_USB;
 451                        break;
 452                case SM5502_REG_DEV_TYPE1_DEDICATED_CHG_MASK:
 453                        cable_type = SM5502_MUIC_ADC_OPEN_TA;
 454                        break;
 455                default:
 456                        dev_dbg(info->dev,
 457                                "cannot identify the cable type: adc(0x%x)\n",
 458                                adc);
 459                        return -EINVAL;
 460                }
 461                break;
 462        default:
 463                dev_err(info->dev,
 464                        "failed to identify the cable type: adc(0x%x)\n", adc);
 465                return -EINVAL;
 466        }
 467
 468        return cable_type;
 469}
 470
 471static int sm5502_muic_cable_handler(struct sm5502_muic_info *info,
 472                                     bool attached)
 473{
 474        static unsigned int prev_cable_type = SM5502_MUIC_ADC_GROUND;
 475        unsigned int cable_type = SM5502_MUIC_ADC_GROUND;
 476        unsigned int con_sw = DM_DP_SWITCH_OPEN;
 477        unsigned int vbus_sw = VBUSIN_SWITCH_OPEN;
 478        unsigned int id;
 479        int ret;
 480
 481        /* Get the type of attached or detached cable */
 482        if (attached)
 483                cable_type = sm5502_muic_get_cable_type(info);
 484        else
 485                cable_type = prev_cable_type;
 486        prev_cable_type = cable_type;
 487
 488        switch (cable_type) {
 489        case SM5502_MUIC_ADC_OPEN_USB:
 490                id      = EXTCON_USB;
 491                con_sw  = DM_DP_SWITCH_USB;
 492                vbus_sw = VBUSIN_SWITCH_VBUSOUT_WITH_USB;
 493                break;
 494        case SM5502_MUIC_ADC_OPEN_TA:
 495                id      = EXTCON_CHG_USB_DCP;
 496                con_sw  = DM_DP_SWITCH_OPEN;
 497                vbus_sw = VBUSIN_SWITCH_VBUSOUT;
 498                break;
 499        case SM5502_MUIC_ADC_GROUND_USB_OTG:
 500        case SM5502_MUIC_ADC_OPEN_USB_OTG:
 501                id      = EXTCON_USB_HOST;
 502                con_sw  = DM_DP_SWITCH_USB;
 503                vbus_sw = VBUSIN_SWITCH_OPEN;
 504                break;
 505        default:
 506                dev_dbg(info->dev,
 507                        "cannot handle this cable_type (0x%x)\n", cable_type);
 508                return 0;
 509        }
 510
 511        /* Change internal hardware path(DM_CON/DP_CON, VBUSIN) */
 512        ret = sm5502_muic_set_path(info, con_sw, vbus_sw, attached);
 513        if (ret < 0)
 514                return ret;
 515
 516        /* Change the state of external accessory */
 517        extcon_set_state_sync(info->edev, id, attached);
 518        if (id == EXTCON_USB)
 519                extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SDP,
 520                                        attached);
 521
 522        return 0;
 523}
 524
 525static void sm5502_muic_irq_work(struct work_struct *work)
 526{
 527        struct sm5502_muic_info *info = container_of(work,
 528                        struct sm5502_muic_info, irq_work);
 529        int ret = 0;
 530
 531        if (!info->edev)
 532                return;
 533
 534        mutex_lock(&info->mutex);
 535
 536        /* Detect attached or detached cables */
 537        if (info->irq_attach) {
 538                ret = sm5502_muic_cable_handler(info, true);
 539                info->irq_attach = false;
 540        }
 541        if (info->irq_detach) {
 542                ret = sm5502_muic_cable_handler(info, false);
 543                info->irq_detach = false;
 544        }
 545
 546        if (ret < 0)
 547                dev_err(info->dev, "failed to handle MUIC interrupt\n");
 548
 549        mutex_unlock(&info->mutex);
 550}
 551
 552/*
 553 * Sets irq_attach or irq_detach in sm5502_muic_info and returns 0.
 554 * Returns -ESRCH if irq_type does not match registered IRQ for this dev type.
 555 */
 556static int sm5502_parse_irq(struct sm5502_muic_info *info, int irq_type)
 557{
 558        switch (irq_type) {
 559        case SM5502_IRQ_INT1_ATTACH:
 560                info->irq_attach = true;
 561                break;
 562        case SM5502_IRQ_INT1_DETACH:
 563                info->irq_detach = true;
 564                break;
 565        case SM5502_IRQ_INT1_KP:
 566        case SM5502_IRQ_INT1_LKP:
 567        case SM5502_IRQ_INT1_LKR:
 568        case SM5502_IRQ_INT1_OVP_EVENT:
 569        case SM5502_IRQ_INT1_OCP_EVENT:
 570        case SM5502_IRQ_INT1_OVP_OCP_DIS:
 571        case SM5502_IRQ_INT2_VBUS_DET:
 572        case SM5502_IRQ_INT2_REV_ACCE:
 573        case SM5502_IRQ_INT2_ADC_CHG:
 574        case SM5502_IRQ_INT2_STUCK_KEY:
 575        case SM5502_IRQ_INT2_STUCK_KEY_RCV:
 576        case SM5502_IRQ_INT2_MHL:
 577        default:
 578                break;
 579        }
 580
 581        return 0;
 582}
 583
 584static int sm5504_parse_irq(struct sm5502_muic_info *info, int irq_type)
 585{
 586        switch (irq_type) {
 587        case SM5504_IRQ_INT1_ATTACH:
 588                info->irq_attach = true;
 589                break;
 590        case SM5504_IRQ_INT1_DETACH:
 591                info->irq_detach = true;
 592                break;
 593        case SM5504_IRQ_INT1_CHG_DET:
 594        case SM5504_IRQ_INT1_DCD_OUT:
 595        case SM5504_IRQ_INT1_OVP_EVENT:
 596        case SM5504_IRQ_INT1_CONNECT:
 597        case SM5504_IRQ_INT1_ADC_CHG:
 598        case SM5504_IRQ_INT2_RID_CHG:
 599        case SM5504_IRQ_INT2_UVLO:
 600        case SM5504_IRQ_INT2_POR:
 601        case SM5504_IRQ_INT2_OVP_FET:
 602        case SM5504_IRQ_INT2_OCP_LATCH:
 603        case SM5504_IRQ_INT2_OCP_EVENT:
 604        case SM5504_IRQ_INT2_OVP_OCP_EVENT:
 605        default:
 606                break;
 607        }
 608
 609        return 0;
 610}
 611
 612static irqreturn_t sm5502_muic_irq_handler(int irq, void *data)
 613{
 614        struct sm5502_muic_info *info = data;
 615        int i, irq_type = -1, ret;
 616
 617        for (i = 0; i < info->type->num_muic_irqs; i++)
 618                if (irq == info->type->muic_irqs[i].virq)
 619                        irq_type = info->type->muic_irqs[i].irq;
 620
 621        ret = info->type->parse_irq(info, irq_type);
 622        if (ret < 0) {
 623                dev_warn(info->dev, "cannot handle is interrupt:%d\n",
 624                                    irq_type);
 625                return IRQ_HANDLED;
 626        }
 627        schedule_work(&info->irq_work);
 628
 629        return IRQ_HANDLED;
 630}
 631
 632static void sm5502_muic_detect_cable_wq(struct work_struct *work)
 633{
 634        struct sm5502_muic_info *info = container_of(to_delayed_work(work),
 635                                struct sm5502_muic_info, wq_detcable);
 636        int ret;
 637
 638        /* Notify the state of connector cable or not  */
 639        ret = sm5502_muic_cable_handler(info, true);
 640        if (ret < 0)
 641                dev_warn(info->dev, "failed to detect cable state\n");
 642}
 643
 644static void sm5502_init_dev_type(struct sm5502_muic_info *info)
 645{
 646        unsigned int reg_data, vendor_id, version_id;
 647        int i, ret;
 648
 649        /* To test I2C, Print version_id and vendor_id of SM5502 */
 650        ret = regmap_read(info->regmap, SM5502_REG_DEVICE_ID, &reg_data);
 651        if (ret) {
 652                dev_err(info->dev,
 653                        "failed to read DEVICE_ID register: %d\n", ret);
 654                return;
 655        }
 656
 657        vendor_id = ((reg_data & SM5502_REG_DEVICE_ID_VENDOR_MASK) >>
 658                                SM5502_REG_DEVICE_ID_VENDOR_SHIFT);
 659        version_id = ((reg_data & SM5502_REG_DEVICE_ID_VERSION_MASK) >>
 660                                SM5502_REG_DEVICE_ID_VERSION_SHIFT);
 661
 662        dev_info(info->dev, "Device type: version: 0x%x, vendor: 0x%x\n",
 663                            version_id, vendor_id);
 664
 665        /* Initiazle the register of SM5502 device to bring-up */
 666        for (i = 0; i < info->type->num_reg_data; i++) {
 667                unsigned int val = 0;
 668
 669                if (!info->type->reg_data[i].invert)
 670                        val |= ~info->type->reg_data[i].val;
 671                else
 672                        val = info->type->reg_data[i].val;
 673                regmap_write(info->regmap, info->type->reg_data[i].reg, val);
 674        }
 675}
 676
 677static int sm5022_muic_i2c_probe(struct i2c_client *i2c)
 678{
 679        struct device_node *np = i2c->dev.of_node;
 680        struct sm5502_muic_info *info;
 681        int i, ret, irq_flags;
 682
 683        if (!np)
 684                return -EINVAL;
 685
 686        info = devm_kzalloc(&i2c->dev, sizeof(*info), GFP_KERNEL);
 687        if (!info)
 688                return -ENOMEM;
 689        i2c_set_clientdata(i2c, info);
 690
 691        info->dev = &i2c->dev;
 692        info->i2c = i2c;
 693        info->irq = i2c->irq;
 694        info->type = device_get_match_data(info->dev);
 695        if (!info->type)
 696                return -EINVAL;
 697        if (!info->type->parse_irq) {
 698                dev_err(info->dev, "parse_irq missing in struct sm5502_type\n");
 699                return -EINVAL;
 700        }
 701
 702        mutex_init(&info->mutex);
 703
 704        INIT_WORK(&info->irq_work, sm5502_muic_irq_work);
 705
 706        info->regmap = devm_regmap_init_i2c(i2c, &sm5502_muic_regmap_config);
 707        if (IS_ERR(info->regmap)) {
 708                ret = PTR_ERR(info->regmap);
 709                dev_err(info->dev, "failed to allocate register map: %d\n",
 710                                   ret);
 711                return ret;
 712        }
 713
 714        /* Support irq domain for SM5502 MUIC device */
 715        irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT | IRQF_SHARED;
 716        ret = devm_regmap_add_irq_chip(info->dev, info->regmap, info->irq,
 717                                       irq_flags, 0, info->type->irq_chip,
 718                                       &info->irq_data);
 719        if (ret != 0) {
 720                dev_err(info->dev, "failed to request IRQ %d: %d\n",
 721                                    info->irq, ret);
 722                return ret;
 723        }
 724
 725        for (i = 0; i < info->type->num_muic_irqs; i++) {
 726                struct muic_irq *muic_irq = &info->type->muic_irqs[i];
 727                int virq = 0;
 728
 729                virq = regmap_irq_get_virq(info->irq_data, muic_irq->irq);
 730                if (virq <= 0)
 731                        return -EINVAL;
 732                muic_irq->virq = virq;
 733
 734                ret = devm_request_threaded_irq(info->dev, virq, NULL,
 735                                                sm5502_muic_irq_handler,
 736                                                IRQF_NO_SUSPEND | IRQF_ONESHOT,
 737                                                muic_irq->name, info);
 738                if (ret) {
 739                        dev_err(info->dev,
 740                                "failed: irq request (IRQ: %d, error :%d)\n",
 741                                muic_irq->irq, ret);
 742                        return ret;
 743                }
 744        }
 745
 746        /* Allocate extcon device */
 747        info->edev = devm_extcon_dev_allocate(info->dev, sm5502_extcon_cable);
 748        if (IS_ERR(info->edev)) {
 749                dev_err(info->dev, "failed to allocate memory for extcon\n");
 750                return -ENOMEM;
 751        }
 752
 753        /* Register extcon device */
 754        ret = devm_extcon_dev_register(info->dev, info->edev);
 755        if (ret) {
 756                dev_err(info->dev, "failed to register extcon device\n");
 757                return ret;
 758        }
 759
 760        /*
 761         * Detect accessory after completing the initialization of platform
 762         *
 763         * - Use delayed workqueue to detect cable state and then
 764         * notify cable state to notifiee/platform through uevent.
 765         * After completing the booting of platform, the extcon provider
 766         * driver should notify cable state to upper layer.
 767         */
 768        INIT_DELAYED_WORK(&info->wq_detcable, sm5502_muic_detect_cable_wq);
 769        queue_delayed_work(system_power_efficient_wq, &info->wq_detcable,
 770                        msecs_to_jiffies(DELAY_MS_DEFAULT));
 771
 772        /* Initialize SM5502 device and print vendor id and version id */
 773        sm5502_init_dev_type(info);
 774
 775        return 0;
 776}
 777
 778static const struct sm5502_type sm5502_data = {
 779        .muic_irqs = sm5502_muic_irqs,
 780        .num_muic_irqs = ARRAY_SIZE(sm5502_muic_irqs),
 781        .irq_chip = &sm5502_muic_irq_chip,
 782        .reg_data = sm5502_reg_data,
 783        .num_reg_data = ARRAY_SIZE(sm5502_reg_data),
 784        .otg_dev_type1 = SM5502_REG_DEV_TYPE1_USB_OTG_MASK,
 785        .parse_irq = sm5502_parse_irq,
 786};
 787
 788static const struct sm5502_type sm5504_data = {
 789        .muic_irqs = sm5504_muic_irqs,
 790        .num_muic_irqs = ARRAY_SIZE(sm5504_muic_irqs),
 791        .irq_chip = &sm5504_muic_irq_chip,
 792        .reg_data = sm5504_reg_data,
 793        .num_reg_data = ARRAY_SIZE(sm5504_reg_data),
 794        .otg_dev_type1 = SM5504_REG_DEV_TYPE1_USB_OTG_MASK,
 795        .parse_irq = sm5504_parse_irq,
 796};
 797
 798static const struct of_device_id sm5502_dt_match[] = {
 799        { .compatible = "siliconmitus,sm5502-muic", .data = &sm5502_data },
 800        { .compatible = "siliconmitus,sm5504-muic", .data = &sm5504_data },
 801        { },
 802};
 803MODULE_DEVICE_TABLE(of, sm5502_dt_match);
 804
 805#ifdef CONFIG_PM_SLEEP
 806static int sm5502_muic_suspend(struct device *dev)
 807{
 808        struct i2c_client *i2c = to_i2c_client(dev);
 809        struct sm5502_muic_info *info = i2c_get_clientdata(i2c);
 810
 811        enable_irq_wake(info->irq);
 812
 813        return 0;
 814}
 815
 816static int sm5502_muic_resume(struct device *dev)
 817{
 818        struct i2c_client *i2c = to_i2c_client(dev);
 819        struct sm5502_muic_info *info = i2c_get_clientdata(i2c);
 820
 821        disable_irq_wake(info->irq);
 822
 823        return 0;
 824}
 825#endif
 826
 827static SIMPLE_DEV_PM_OPS(sm5502_muic_pm_ops,
 828                         sm5502_muic_suspend, sm5502_muic_resume);
 829
 830static const struct i2c_device_id sm5502_i2c_id[] = {
 831        { "sm5502", (kernel_ulong_t)&sm5502_data },
 832        { "sm5504", (kernel_ulong_t)&sm5504_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_new = 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