162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * extcon-sm5502.c - Silicon Mitus SM5502 extcon drvier to support USB switches 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2014 Samsung Electronics Co., Ltd 662306a36Sopenharmony_ci * Author: Chanwoo Choi <cw00.choi@samsung.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/err.h> 1062306a36Sopenharmony_ci#include <linux/i2c.h> 1162306a36Sopenharmony_ci#include <linux/interrupt.h> 1262306a36Sopenharmony_ci#include <linux/irqdomain.h> 1362306a36Sopenharmony_ci#include <linux/kernel.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/platform_device.h> 1662306a36Sopenharmony_ci#include <linux/regmap.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci#include <linux/extcon-provider.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include "extcon-sm5502.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define DELAY_MS_DEFAULT 17000 /* unit: millisecond */ 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistruct muic_irq { 2562306a36Sopenharmony_ci unsigned int irq; 2662306a36Sopenharmony_ci const char *name; 2762306a36Sopenharmony_ci unsigned int virq; 2862306a36Sopenharmony_ci}; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistruct reg_data { 3162306a36Sopenharmony_ci u8 reg; 3262306a36Sopenharmony_ci unsigned int val; 3362306a36Sopenharmony_ci bool invert; 3462306a36Sopenharmony_ci}; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistruct sm5502_muic_info { 3762306a36Sopenharmony_ci struct device *dev; 3862306a36Sopenharmony_ci struct extcon_dev *edev; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci struct i2c_client *i2c; 4162306a36Sopenharmony_ci struct regmap *regmap; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci const struct sm5502_type *type; 4462306a36Sopenharmony_ci struct regmap_irq_chip_data *irq_data; 4562306a36Sopenharmony_ci int irq; 4662306a36Sopenharmony_ci bool irq_attach; 4762306a36Sopenharmony_ci bool irq_detach; 4862306a36Sopenharmony_ci struct work_struct irq_work; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci struct mutex mutex; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci /* 5362306a36Sopenharmony_ci * Use delayed workqueue to detect cable state and then 5462306a36Sopenharmony_ci * notify cable state to notifiee/platform through uevent. 5562306a36Sopenharmony_ci * After completing the booting of platform, the extcon provider 5662306a36Sopenharmony_ci * driver should notify cable state to upper layer. 5762306a36Sopenharmony_ci */ 5862306a36Sopenharmony_ci struct delayed_work wq_detcable; 5962306a36Sopenharmony_ci}; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistruct sm5502_type { 6262306a36Sopenharmony_ci struct muic_irq *muic_irqs; 6362306a36Sopenharmony_ci unsigned int num_muic_irqs; 6462306a36Sopenharmony_ci const struct regmap_irq_chip *irq_chip; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci struct reg_data *reg_data; 6762306a36Sopenharmony_ci unsigned int num_reg_data; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci unsigned int otg_dev_type1; 7062306a36Sopenharmony_ci int (*parse_irq)(struct sm5502_muic_info *info, int irq_type); 7162306a36Sopenharmony_ci}; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci/* Default value of SM5502 register to bring up MUIC device. */ 7462306a36Sopenharmony_cistatic struct reg_data sm5502_reg_data[] = { 7562306a36Sopenharmony_ci { 7662306a36Sopenharmony_ci .reg = SM5502_REG_RESET, 7762306a36Sopenharmony_ci .val = SM5502_REG_RESET_MASK, 7862306a36Sopenharmony_ci .invert = true, 7962306a36Sopenharmony_ci }, { 8062306a36Sopenharmony_ci .reg = SM5502_REG_CONTROL, 8162306a36Sopenharmony_ci .val = SM5502_REG_CONTROL_MASK_INT_MASK, 8262306a36Sopenharmony_ci .invert = false, 8362306a36Sopenharmony_ci }, { 8462306a36Sopenharmony_ci .reg = SM5502_REG_INTMASK1, 8562306a36Sopenharmony_ci .val = SM5502_REG_INTM1_KP_MASK 8662306a36Sopenharmony_ci | SM5502_REG_INTM1_LKP_MASK 8762306a36Sopenharmony_ci | SM5502_REG_INTM1_LKR_MASK, 8862306a36Sopenharmony_ci .invert = true, 8962306a36Sopenharmony_ci }, { 9062306a36Sopenharmony_ci .reg = SM5502_REG_INTMASK2, 9162306a36Sopenharmony_ci .val = SM5502_REG_INTM2_VBUS_DET_MASK 9262306a36Sopenharmony_ci | SM5502_REG_INTM2_REV_ACCE_MASK 9362306a36Sopenharmony_ci | SM5502_REG_INTM2_ADC_CHG_MASK 9462306a36Sopenharmony_ci | SM5502_REG_INTM2_STUCK_KEY_MASK 9562306a36Sopenharmony_ci | SM5502_REG_INTM2_STUCK_KEY_RCV_MASK 9662306a36Sopenharmony_ci | SM5502_REG_INTM2_MHL_MASK, 9762306a36Sopenharmony_ci .invert = true, 9862306a36Sopenharmony_ci }, 9962306a36Sopenharmony_ci}; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci/* Default value of SM5504 register to bring up MUIC device. */ 10262306a36Sopenharmony_cistatic struct reg_data sm5504_reg_data[] = { 10362306a36Sopenharmony_ci { 10462306a36Sopenharmony_ci .reg = SM5502_REG_RESET, 10562306a36Sopenharmony_ci .val = SM5502_REG_RESET_MASK, 10662306a36Sopenharmony_ci .invert = true, 10762306a36Sopenharmony_ci }, { 10862306a36Sopenharmony_ci .reg = SM5502_REG_INTMASK1, 10962306a36Sopenharmony_ci .val = SM5504_REG_INTM1_ATTACH_MASK 11062306a36Sopenharmony_ci | SM5504_REG_INTM1_DETACH_MASK, 11162306a36Sopenharmony_ci .invert = false, 11262306a36Sopenharmony_ci }, { 11362306a36Sopenharmony_ci .reg = SM5502_REG_INTMASK2, 11462306a36Sopenharmony_ci .val = SM5504_REG_INTM2_RID_CHG_MASK 11562306a36Sopenharmony_ci | SM5504_REG_INTM2_UVLO_MASK 11662306a36Sopenharmony_ci | SM5504_REG_INTM2_POR_MASK, 11762306a36Sopenharmony_ci .invert = true, 11862306a36Sopenharmony_ci }, { 11962306a36Sopenharmony_ci .reg = SM5502_REG_CONTROL, 12062306a36Sopenharmony_ci .val = SM5502_REG_CONTROL_MANUAL_SW_MASK 12162306a36Sopenharmony_ci | SM5504_REG_CONTROL_CHGTYP_MASK 12262306a36Sopenharmony_ci | SM5504_REG_CONTROL_USBCHDEN_MASK 12362306a36Sopenharmony_ci | SM5504_REG_CONTROL_ADC_EN_MASK, 12462306a36Sopenharmony_ci .invert = true, 12562306a36Sopenharmony_ci }, 12662306a36Sopenharmony_ci}; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci/* List of detectable cables */ 12962306a36Sopenharmony_cistatic const unsigned int sm5502_extcon_cable[] = { 13062306a36Sopenharmony_ci EXTCON_USB, 13162306a36Sopenharmony_ci EXTCON_USB_HOST, 13262306a36Sopenharmony_ci EXTCON_CHG_USB_SDP, 13362306a36Sopenharmony_ci EXTCON_CHG_USB_DCP, 13462306a36Sopenharmony_ci EXTCON_NONE, 13562306a36Sopenharmony_ci}; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci/* Define supported accessory type */ 13862306a36Sopenharmony_cienum sm5502_muic_acc_type { 13962306a36Sopenharmony_ci SM5502_MUIC_ADC_GROUND = 0x0, 14062306a36Sopenharmony_ci SM5502_MUIC_ADC_SEND_END_BUTTON, 14162306a36Sopenharmony_ci SM5502_MUIC_ADC_REMOTE_S1_BUTTON, 14262306a36Sopenharmony_ci SM5502_MUIC_ADC_REMOTE_S2_BUTTON, 14362306a36Sopenharmony_ci SM5502_MUIC_ADC_REMOTE_S3_BUTTON, 14462306a36Sopenharmony_ci SM5502_MUIC_ADC_REMOTE_S4_BUTTON, 14562306a36Sopenharmony_ci SM5502_MUIC_ADC_REMOTE_S5_BUTTON, 14662306a36Sopenharmony_ci SM5502_MUIC_ADC_REMOTE_S6_BUTTON, 14762306a36Sopenharmony_ci SM5502_MUIC_ADC_REMOTE_S7_BUTTON, 14862306a36Sopenharmony_ci SM5502_MUIC_ADC_REMOTE_S8_BUTTON, 14962306a36Sopenharmony_ci SM5502_MUIC_ADC_REMOTE_S9_BUTTON, 15062306a36Sopenharmony_ci SM5502_MUIC_ADC_REMOTE_S10_BUTTON, 15162306a36Sopenharmony_ci SM5502_MUIC_ADC_REMOTE_S11_BUTTON, 15262306a36Sopenharmony_ci SM5502_MUIC_ADC_REMOTE_S12_BUTTON, 15362306a36Sopenharmony_ci SM5502_MUIC_ADC_RESERVED_ACC_1, 15462306a36Sopenharmony_ci SM5502_MUIC_ADC_RESERVED_ACC_2, 15562306a36Sopenharmony_ci SM5502_MUIC_ADC_RESERVED_ACC_3, 15662306a36Sopenharmony_ci SM5502_MUIC_ADC_RESERVED_ACC_4, 15762306a36Sopenharmony_ci SM5502_MUIC_ADC_RESERVED_ACC_5, 15862306a36Sopenharmony_ci SM5502_MUIC_ADC_AUDIO_TYPE2, 15962306a36Sopenharmony_ci SM5502_MUIC_ADC_PHONE_POWERED_DEV, 16062306a36Sopenharmony_ci SM5502_MUIC_ADC_TTY_CONVERTER, 16162306a36Sopenharmony_ci SM5502_MUIC_ADC_UART_CABLE, 16262306a36Sopenharmony_ci SM5502_MUIC_ADC_TYPE1_CHARGER, 16362306a36Sopenharmony_ci SM5502_MUIC_ADC_FACTORY_MODE_BOOT_OFF_USB, 16462306a36Sopenharmony_ci SM5502_MUIC_ADC_FACTORY_MODE_BOOT_ON_USB, 16562306a36Sopenharmony_ci SM5502_MUIC_ADC_AUDIO_VIDEO_CABLE, 16662306a36Sopenharmony_ci SM5502_MUIC_ADC_TYPE2_CHARGER, 16762306a36Sopenharmony_ci SM5502_MUIC_ADC_FACTORY_MODE_BOOT_OFF_UART, 16862306a36Sopenharmony_ci SM5502_MUIC_ADC_FACTORY_MODE_BOOT_ON_UART, 16962306a36Sopenharmony_ci SM5502_MUIC_ADC_AUDIO_TYPE1, 17062306a36Sopenharmony_ci SM5502_MUIC_ADC_OPEN = 0x1f, 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci /* 17362306a36Sopenharmony_ci * The below accessories have same ADC value (0x1f or 0x1e). 17462306a36Sopenharmony_ci * So, Device type1 is used to separate specific accessory. 17562306a36Sopenharmony_ci */ 17662306a36Sopenharmony_ci /* |---------|--ADC| */ 17762306a36Sopenharmony_ci /* | [7:5]|[4:0]| */ 17862306a36Sopenharmony_ci SM5502_MUIC_ADC_AUDIO_TYPE1_FULL_REMOTE = 0x3e, /* | 001|11110| */ 17962306a36Sopenharmony_ci SM5502_MUIC_ADC_AUDIO_TYPE1_SEND_END = 0x5e, /* | 010|11110| */ 18062306a36Sopenharmony_ci /* |Dev Type1|--ADC| */ 18162306a36Sopenharmony_ci SM5502_MUIC_ADC_GROUND_USB_OTG = 0x80, /* | 100|00000| */ 18262306a36Sopenharmony_ci SM5502_MUIC_ADC_OPEN_USB = 0x5f, /* | 010|11111| */ 18362306a36Sopenharmony_ci SM5502_MUIC_ADC_OPEN_TA = 0xdf, /* | 110|11111| */ 18462306a36Sopenharmony_ci SM5502_MUIC_ADC_OPEN_USB_OTG = 0xff, /* | 111|11111| */ 18562306a36Sopenharmony_ci}; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci/* List of supported interrupt for SM5502 */ 18862306a36Sopenharmony_cistatic struct muic_irq sm5502_muic_irqs[] = { 18962306a36Sopenharmony_ci { SM5502_IRQ_INT1_ATTACH, "muic-attach" }, 19062306a36Sopenharmony_ci { SM5502_IRQ_INT1_DETACH, "muic-detach" }, 19162306a36Sopenharmony_ci { SM5502_IRQ_INT1_KP, "muic-kp" }, 19262306a36Sopenharmony_ci { SM5502_IRQ_INT1_LKP, "muic-lkp" }, 19362306a36Sopenharmony_ci { SM5502_IRQ_INT1_LKR, "muic-lkr" }, 19462306a36Sopenharmony_ci { SM5502_IRQ_INT1_OVP_EVENT, "muic-ovp-event" }, 19562306a36Sopenharmony_ci { SM5502_IRQ_INT1_OCP_EVENT, "muic-ocp-event" }, 19662306a36Sopenharmony_ci { SM5502_IRQ_INT1_OVP_OCP_DIS, "muic-ovp-ocp-dis" }, 19762306a36Sopenharmony_ci { SM5502_IRQ_INT2_VBUS_DET, "muic-vbus-det" }, 19862306a36Sopenharmony_ci { SM5502_IRQ_INT2_REV_ACCE, "muic-rev-acce" }, 19962306a36Sopenharmony_ci { SM5502_IRQ_INT2_ADC_CHG, "muic-adc-chg" }, 20062306a36Sopenharmony_ci { SM5502_IRQ_INT2_STUCK_KEY, "muic-stuck-key" }, 20162306a36Sopenharmony_ci { SM5502_IRQ_INT2_STUCK_KEY_RCV, "muic-stuck-key-rcv" }, 20262306a36Sopenharmony_ci { SM5502_IRQ_INT2_MHL, "muic-mhl" }, 20362306a36Sopenharmony_ci}; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci/* Define interrupt list of SM5502 to register regmap_irq */ 20662306a36Sopenharmony_cistatic const struct regmap_irq sm5502_irqs[] = { 20762306a36Sopenharmony_ci /* INT1 interrupts */ 20862306a36Sopenharmony_ci { .reg_offset = 0, .mask = SM5502_IRQ_INT1_ATTACH_MASK, }, 20962306a36Sopenharmony_ci { .reg_offset = 0, .mask = SM5502_IRQ_INT1_DETACH_MASK, }, 21062306a36Sopenharmony_ci { .reg_offset = 0, .mask = SM5502_IRQ_INT1_KP_MASK, }, 21162306a36Sopenharmony_ci { .reg_offset = 0, .mask = SM5502_IRQ_INT1_LKP_MASK, }, 21262306a36Sopenharmony_ci { .reg_offset = 0, .mask = SM5502_IRQ_INT1_LKR_MASK, }, 21362306a36Sopenharmony_ci { .reg_offset = 0, .mask = SM5502_IRQ_INT1_OVP_EVENT_MASK, }, 21462306a36Sopenharmony_ci { .reg_offset = 0, .mask = SM5502_IRQ_INT1_OCP_EVENT_MASK, }, 21562306a36Sopenharmony_ci { .reg_offset = 0, .mask = SM5502_IRQ_INT1_OVP_OCP_DIS_MASK, }, 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci /* INT2 interrupts */ 21862306a36Sopenharmony_ci { .reg_offset = 1, .mask = SM5502_IRQ_INT2_VBUS_DET_MASK,}, 21962306a36Sopenharmony_ci { .reg_offset = 1, .mask = SM5502_IRQ_INT2_REV_ACCE_MASK, }, 22062306a36Sopenharmony_ci { .reg_offset = 1, .mask = SM5502_IRQ_INT2_ADC_CHG_MASK, }, 22162306a36Sopenharmony_ci { .reg_offset = 1, .mask = SM5502_IRQ_INT2_STUCK_KEY_MASK, }, 22262306a36Sopenharmony_ci { .reg_offset = 1, .mask = SM5502_IRQ_INT2_STUCK_KEY_RCV_MASK, }, 22362306a36Sopenharmony_ci { .reg_offset = 1, .mask = SM5502_IRQ_INT2_MHL_MASK, }, 22462306a36Sopenharmony_ci}; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic const struct regmap_irq_chip sm5502_muic_irq_chip = { 22762306a36Sopenharmony_ci .name = "sm5502", 22862306a36Sopenharmony_ci .status_base = SM5502_REG_INT1, 22962306a36Sopenharmony_ci .mask_base = SM5502_REG_INTMASK1, 23062306a36Sopenharmony_ci .num_regs = 2, 23162306a36Sopenharmony_ci .irqs = sm5502_irqs, 23262306a36Sopenharmony_ci .num_irqs = ARRAY_SIZE(sm5502_irqs), 23362306a36Sopenharmony_ci}; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci/* List of supported interrupt for SM5504 */ 23662306a36Sopenharmony_cistatic struct muic_irq sm5504_muic_irqs[] = { 23762306a36Sopenharmony_ci { SM5504_IRQ_INT1_ATTACH, "muic-attach" }, 23862306a36Sopenharmony_ci { SM5504_IRQ_INT1_DETACH, "muic-detach" }, 23962306a36Sopenharmony_ci { SM5504_IRQ_INT1_CHG_DET, "muic-chg-det" }, 24062306a36Sopenharmony_ci { SM5504_IRQ_INT1_DCD_OUT, "muic-dcd-out" }, 24162306a36Sopenharmony_ci { SM5504_IRQ_INT1_OVP_EVENT, "muic-ovp-event" }, 24262306a36Sopenharmony_ci { SM5504_IRQ_INT1_CONNECT, "muic-connect" }, 24362306a36Sopenharmony_ci { SM5504_IRQ_INT1_ADC_CHG, "muic-adc-chg" }, 24462306a36Sopenharmony_ci { SM5504_IRQ_INT2_RID_CHG, "muic-rid-chg" }, 24562306a36Sopenharmony_ci { SM5504_IRQ_INT2_UVLO, "muic-uvlo" }, 24662306a36Sopenharmony_ci { SM5504_IRQ_INT2_POR, "muic-por" }, 24762306a36Sopenharmony_ci { SM5504_IRQ_INT2_OVP_FET, "muic-ovp-fet" }, 24862306a36Sopenharmony_ci { SM5504_IRQ_INT2_OCP_LATCH, "muic-ocp-latch" }, 24962306a36Sopenharmony_ci { SM5504_IRQ_INT2_OCP_EVENT, "muic-ocp-event" }, 25062306a36Sopenharmony_ci { SM5504_IRQ_INT2_OVP_OCP_EVENT, "muic-ovp-ocp-event" }, 25162306a36Sopenharmony_ci}; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci/* Define interrupt list of SM5504 to register regmap_irq */ 25462306a36Sopenharmony_cistatic const struct regmap_irq sm5504_irqs[] = { 25562306a36Sopenharmony_ci /* INT1 interrupts */ 25662306a36Sopenharmony_ci { .reg_offset = 0, .mask = SM5504_IRQ_INT1_ATTACH_MASK, }, 25762306a36Sopenharmony_ci { .reg_offset = 0, .mask = SM5504_IRQ_INT1_DETACH_MASK, }, 25862306a36Sopenharmony_ci { .reg_offset = 0, .mask = SM5504_IRQ_INT1_CHG_DET_MASK, }, 25962306a36Sopenharmony_ci { .reg_offset = 0, .mask = SM5504_IRQ_INT1_DCD_OUT_MASK, }, 26062306a36Sopenharmony_ci { .reg_offset = 0, .mask = SM5504_IRQ_INT1_OVP_MASK, }, 26162306a36Sopenharmony_ci { .reg_offset = 0, .mask = SM5504_IRQ_INT1_CONNECT_MASK, }, 26262306a36Sopenharmony_ci { .reg_offset = 0, .mask = SM5504_IRQ_INT1_ADC_CHG_MASK, }, 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci /* INT2 interrupts */ 26562306a36Sopenharmony_ci { .reg_offset = 1, .mask = SM5504_IRQ_INT2_RID_CHG_MASK,}, 26662306a36Sopenharmony_ci { .reg_offset = 1, .mask = SM5504_IRQ_INT2_UVLO_MASK, }, 26762306a36Sopenharmony_ci { .reg_offset = 1, .mask = SM5504_IRQ_INT2_POR_MASK, }, 26862306a36Sopenharmony_ci { .reg_offset = 1, .mask = SM5504_IRQ_INT2_OVP_FET_MASK, }, 26962306a36Sopenharmony_ci { .reg_offset = 1, .mask = SM5504_IRQ_INT2_OCP_LATCH_MASK, }, 27062306a36Sopenharmony_ci { .reg_offset = 1, .mask = SM5504_IRQ_INT2_OCP_EVENT_MASK, }, 27162306a36Sopenharmony_ci { .reg_offset = 1, .mask = SM5504_IRQ_INT2_OVP_OCP_EVENT_MASK, }, 27262306a36Sopenharmony_ci}; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_cistatic const struct regmap_irq_chip sm5504_muic_irq_chip = { 27562306a36Sopenharmony_ci .name = "sm5504", 27662306a36Sopenharmony_ci .status_base = SM5502_REG_INT1, 27762306a36Sopenharmony_ci .mask_base = SM5502_REG_INTMASK1, 27862306a36Sopenharmony_ci .num_regs = 2, 27962306a36Sopenharmony_ci .irqs = sm5504_irqs, 28062306a36Sopenharmony_ci .num_irqs = ARRAY_SIZE(sm5504_irqs), 28162306a36Sopenharmony_ci}; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci/* Define regmap configuration of SM5502 for I2C communication */ 28462306a36Sopenharmony_cistatic bool sm5502_muic_volatile_reg(struct device *dev, unsigned int reg) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci switch (reg) { 28762306a36Sopenharmony_ci case SM5502_REG_INTMASK1: 28862306a36Sopenharmony_ci case SM5502_REG_INTMASK2: 28962306a36Sopenharmony_ci return true; 29062306a36Sopenharmony_ci default: 29162306a36Sopenharmony_ci break; 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci return false; 29462306a36Sopenharmony_ci} 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_cistatic const struct regmap_config sm5502_muic_regmap_config = { 29762306a36Sopenharmony_ci .reg_bits = 8, 29862306a36Sopenharmony_ci .val_bits = 8, 29962306a36Sopenharmony_ci .volatile_reg = sm5502_muic_volatile_reg, 30062306a36Sopenharmony_ci .max_register = SM5502_REG_END, 30162306a36Sopenharmony_ci}; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci/* Change DM_CON/DP_CON/VBUSIN switch according to cable type */ 30462306a36Sopenharmony_cistatic int sm5502_muic_set_path(struct sm5502_muic_info *info, 30562306a36Sopenharmony_ci unsigned int con_sw, unsigned int vbus_sw, 30662306a36Sopenharmony_ci bool attached) 30762306a36Sopenharmony_ci{ 30862306a36Sopenharmony_ci int ret; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci if (!attached) { 31162306a36Sopenharmony_ci con_sw = DM_DP_SWITCH_OPEN; 31262306a36Sopenharmony_ci vbus_sw = VBUSIN_SWITCH_OPEN; 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci switch (con_sw) { 31662306a36Sopenharmony_ci case DM_DP_SWITCH_OPEN: 31762306a36Sopenharmony_ci case DM_DP_SWITCH_USB: 31862306a36Sopenharmony_ci case DM_DP_SWITCH_AUDIO: 31962306a36Sopenharmony_ci case DM_DP_SWITCH_UART: 32062306a36Sopenharmony_ci ret = regmap_update_bits(info->regmap, SM5502_REG_MANUAL_SW1, 32162306a36Sopenharmony_ci SM5502_REG_MANUAL_SW1_DP_MASK | 32262306a36Sopenharmony_ci SM5502_REG_MANUAL_SW1_DM_MASK, 32362306a36Sopenharmony_ci con_sw); 32462306a36Sopenharmony_ci if (ret < 0) { 32562306a36Sopenharmony_ci dev_err(info->dev, 32662306a36Sopenharmony_ci "cannot update DM_CON/DP_CON switch\n"); 32762306a36Sopenharmony_ci return ret; 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci break; 33062306a36Sopenharmony_ci default: 33162306a36Sopenharmony_ci dev_err(info->dev, "Unknown DM_CON/DP_CON switch type (%d)\n", 33262306a36Sopenharmony_ci con_sw); 33362306a36Sopenharmony_ci return -EINVAL; 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci switch (vbus_sw) { 33762306a36Sopenharmony_ci case VBUSIN_SWITCH_OPEN: 33862306a36Sopenharmony_ci case VBUSIN_SWITCH_VBUSOUT: 33962306a36Sopenharmony_ci case VBUSIN_SWITCH_MIC: 34062306a36Sopenharmony_ci case VBUSIN_SWITCH_VBUSOUT_WITH_USB: 34162306a36Sopenharmony_ci ret = regmap_update_bits(info->regmap, SM5502_REG_MANUAL_SW1, 34262306a36Sopenharmony_ci SM5502_REG_MANUAL_SW1_VBUSIN_MASK, 34362306a36Sopenharmony_ci vbus_sw); 34462306a36Sopenharmony_ci if (ret < 0) { 34562306a36Sopenharmony_ci dev_err(info->dev, 34662306a36Sopenharmony_ci "cannot update VBUSIN switch\n"); 34762306a36Sopenharmony_ci return ret; 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci break; 35062306a36Sopenharmony_ci default: 35162306a36Sopenharmony_ci dev_err(info->dev, "Unknown VBUS switch type (%d)\n", vbus_sw); 35262306a36Sopenharmony_ci return -EINVAL; 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci return 0; 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci/* Return cable type of attached or detached accessories */ 35962306a36Sopenharmony_cistatic unsigned int sm5502_muic_get_cable_type(struct sm5502_muic_info *info) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci unsigned int cable_type, adc, dev_type1; 36262306a36Sopenharmony_ci int ret; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci /* Read ADC value according to external cable or button */ 36562306a36Sopenharmony_ci ret = regmap_read(info->regmap, SM5502_REG_ADC, &adc); 36662306a36Sopenharmony_ci if (ret) { 36762306a36Sopenharmony_ci dev_err(info->dev, "failed to read ADC register\n"); 36862306a36Sopenharmony_ci return ret; 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci /* 37262306a36Sopenharmony_ci * If ADC is SM5502_MUIC_ADC_GROUND(0x0), external cable hasn't 37362306a36Sopenharmony_ci * connected with to MUIC device. 37462306a36Sopenharmony_ci */ 37562306a36Sopenharmony_ci cable_type = adc & SM5502_REG_ADC_MASK; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci switch (cable_type) { 37862306a36Sopenharmony_ci case SM5502_MUIC_ADC_GROUND: 37962306a36Sopenharmony_ci ret = regmap_read(info->regmap, SM5502_REG_DEV_TYPE1, 38062306a36Sopenharmony_ci &dev_type1); 38162306a36Sopenharmony_ci if (ret) { 38262306a36Sopenharmony_ci dev_err(info->dev, "failed to read DEV_TYPE1 reg\n"); 38362306a36Sopenharmony_ci return ret; 38462306a36Sopenharmony_ci } 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci if (dev_type1 == info->type->otg_dev_type1) { 38762306a36Sopenharmony_ci cable_type = SM5502_MUIC_ADC_GROUND_USB_OTG; 38862306a36Sopenharmony_ci } else { 38962306a36Sopenharmony_ci dev_dbg(info->dev, 39062306a36Sopenharmony_ci "cannot identify the cable type: adc(0x%x), dev_type1(0x%x)\n", 39162306a36Sopenharmony_ci adc, dev_type1); 39262306a36Sopenharmony_ci return -EINVAL; 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci break; 39562306a36Sopenharmony_ci case SM5502_MUIC_ADC_SEND_END_BUTTON: 39662306a36Sopenharmony_ci case SM5502_MUIC_ADC_REMOTE_S1_BUTTON: 39762306a36Sopenharmony_ci case SM5502_MUIC_ADC_REMOTE_S2_BUTTON: 39862306a36Sopenharmony_ci case SM5502_MUIC_ADC_REMOTE_S3_BUTTON: 39962306a36Sopenharmony_ci case SM5502_MUIC_ADC_REMOTE_S4_BUTTON: 40062306a36Sopenharmony_ci case SM5502_MUIC_ADC_REMOTE_S5_BUTTON: 40162306a36Sopenharmony_ci case SM5502_MUIC_ADC_REMOTE_S6_BUTTON: 40262306a36Sopenharmony_ci case SM5502_MUIC_ADC_REMOTE_S7_BUTTON: 40362306a36Sopenharmony_ci case SM5502_MUIC_ADC_REMOTE_S8_BUTTON: 40462306a36Sopenharmony_ci case SM5502_MUIC_ADC_REMOTE_S9_BUTTON: 40562306a36Sopenharmony_ci case SM5502_MUIC_ADC_REMOTE_S10_BUTTON: 40662306a36Sopenharmony_ci case SM5502_MUIC_ADC_REMOTE_S11_BUTTON: 40762306a36Sopenharmony_ci case SM5502_MUIC_ADC_REMOTE_S12_BUTTON: 40862306a36Sopenharmony_ci case SM5502_MUIC_ADC_RESERVED_ACC_1: 40962306a36Sopenharmony_ci case SM5502_MUIC_ADC_RESERVED_ACC_2: 41062306a36Sopenharmony_ci case SM5502_MUIC_ADC_RESERVED_ACC_3: 41162306a36Sopenharmony_ci case SM5502_MUIC_ADC_RESERVED_ACC_4: 41262306a36Sopenharmony_ci case SM5502_MUIC_ADC_RESERVED_ACC_5: 41362306a36Sopenharmony_ci case SM5502_MUIC_ADC_AUDIO_TYPE2: 41462306a36Sopenharmony_ci case SM5502_MUIC_ADC_PHONE_POWERED_DEV: 41562306a36Sopenharmony_ci case SM5502_MUIC_ADC_TTY_CONVERTER: 41662306a36Sopenharmony_ci case SM5502_MUIC_ADC_UART_CABLE: 41762306a36Sopenharmony_ci case SM5502_MUIC_ADC_TYPE1_CHARGER: 41862306a36Sopenharmony_ci case SM5502_MUIC_ADC_FACTORY_MODE_BOOT_OFF_USB: 41962306a36Sopenharmony_ci case SM5502_MUIC_ADC_FACTORY_MODE_BOOT_ON_USB: 42062306a36Sopenharmony_ci case SM5502_MUIC_ADC_AUDIO_VIDEO_CABLE: 42162306a36Sopenharmony_ci case SM5502_MUIC_ADC_TYPE2_CHARGER: 42262306a36Sopenharmony_ci case SM5502_MUIC_ADC_FACTORY_MODE_BOOT_OFF_UART: 42362306a36Sopenharmony_ci case SM5502_MUIC_ADC_FACTORY_MODE_BOOT_ON_UART: 42462306a36Sopenharmony_ci break; 42562306a36Sopenharmony_ci case SM5502_MUIC_ADC_AUDIO_TYPE1: 42662306a36Sopenharmony_ci /* 42762306a36Sopenharmony_ci * Check whether cable type is 42862306a36Sopenharmony_ci * SM5502_MUIC_ADC_AUDIO_TYPE1_FULL_REMOTE 42962306a36Sopenharmony_ci * or SM5502_MUIC_ADC_AUDIO_TYPE1_SEND_END 43062306a36Sopenharmony_ci * by using Button event. 43162306a36Sopenharmony_ci */ 43262306a36Sopenharmony_ci break; 43362306a36Sopenharmony_ci case SM5502_MUIC_ADC_OPEN: 43462306a36Sopenharmony_ci ret = regmap_read(info->regmap, SM5502_REG_DEV_TYPE1, 43562306a36Sopenharmony_ci &dev_type1); 43662306a36Sopenharmony_ci if (ret) { 43762306a36Sopenharmony_ci dev_err(info->dev, "failed to read DEV_TYPE1 reg\n"); 43862306a36Sopenharmony_ci return ret; 43962306a36Sopenharmony_ci } 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci if (dev_type1 == info->type->otg_dev_type1) { 44262306a36Sopenharmony_ci cable_type = SM5502_MUIC_ADC_OPEN_USB_OTG; 44362306a36Sopenharmony_ci break; 44462306a36Sopenharmony_ci } 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci switch (dev_type1) { 44762306a36Sopenharmony_ci case SM5502_REG_DEV_TYPE1_USB_SDP_MASK: 44862306a36Sopenharmony_ci cable_type = SM5502_MUIC_ADC_OPEN_USB; 44962306a36Sopenharmony_ci break; 45062306a36Sopenharmony_ci case SM5502_REG_DEV_TYPE1_DEDICATED_CHG_MASK: 45162306a36Sopenharmony_ci cable_type = SM5502_MUIC_ADC_OPEN_TA; 45262306a36Sopenharmony_ci break; 45362306a36Sopenharmony_ci default: 45462306a36Sopenharmony_ci dev_dbg(info->dev, 45562306a36Sopenharmony_ci "cannot identify the cable type: adc(0x%x)\n", 45662306a36Sopenharmony_ci adc); 45762306a36Sopenharmony_ci return -EINVAL; 45862306a36Sopenharmony_ci } 45962306a36Sopenharmony_ci break; 46062306a36Sopenharmony_ci default: 46162306a36Sopenharmony_ci dev_err(info->dev, 46262306a36Sopenharmony_ci "failed to identify the cable type: adc(0x%x)\n", adc); 46362306a36Sopenharmony_ci return -EINVAL; 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci return cable_type; 46762306a36Sopenharmony_ci} 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_cistatic int sm5502_muic_cable_handler(struct sm5502_muic_info *info, 47062306a36Sopenharmony_ci bool attached) 47162306a36Sopenharmony_ci{ 47262306a36Sopenharmony_ci static unsigned int prev_cable_type = SM5502_MUIC_ADC_GROUND; 47362306a36Sopenharmony_ci unsigned int cable_type = SM5502_MUIC_ADC_GROUND; 47462306a36Sopenharmony_ci unsigned int con_sw = DM_DP_SWITCH_OPEN; 47562306a36Sopenharmony_ci unsigned int vbus_sw = VBUSIN_SWITCH_OPEN; 47662306a36Sopenharmony_ci unsigned int id; 47762306a36Sopenharmony_ci int ret; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci /* Get the type of attached or detached cable */ 48062306a36Sopenharmony_ci if (attached) 48162306a36Sopenharmony_ci cable_type = sm5502_muic_get_cable_type(info); 48262306a36Sopenharmony_ci else 48362306a36Sopenharmony_ci cable_type = prev_cable_type; 48462306a36Sopenharmony_ci prev_cable_type = cable_type; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci switch (cable_type) { 48762306a36Sopenharmony_ci case SM5502_MUIC_ADC_OPEN_USB: 48862306a36Sopenharmony_ci id = EXTCON_USB; 48962306a36Sopenharmony_ci con_sw = DM_DP_SWITCH_USB; 49062306a36Sopenharmony_ci vbus_sw = VBUSIN_SWITCH_VBUSOUT_WITH_USB; 49162306a36Sopenharmony_ci break; 49262306a36Sopenharmony_ci case SM5502_MUIC_ADC_OPEN_TA: 49362306a36Sopenharmony_ci id = EXTCON_CHG_USB_DCP; 49462306a36Sopenharmony_ci con_sw = DM_DP_SWITCH_OPEN; 49562306a36Sopenharmony_ci vbus_sw = VBUSIN_SWITCH_VBUSOUT; 49662306a36Sopenharmony_ci break; 49762306a36Sopenharmony_ci case SM5502_MUIC_ADC_GROUND_USB_OTG: 49862306a36Sopenharmony_ci case SM5502_MUIC_ADC_OPEN_USB_OTG: 49962306a36Sopenharmony_ci id = EXTCON_USB_HOST; 50062306a36Sopenharmony_ci con_sw = DM_DP_SWITCH_USB; 50162306a36Sopenharmony_ci vbus_sw = VBUSIN_SWITCH_OPEN; 50262306a36Sopenharmony_ci break; 50362306a36Sopenharmony_ci default: 50462306a36Sopenharmony_ci dev_dbg(info->dev, 50562306a36Sopenharmony_ci "cannot handle this cable_type (0x%x)\n", cable_type); 50662306a36Sopenharmony_ci return 0; 50762306a36Sopenharmony_ci } 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci /* Change internal hardware path(DM_CON/DP_CON, VBUSIN) */ 51062306a36Sopenharmony_ci ret = sm5502_muic_set_path(info, con_sw, vbus_sw, attached); 51162306a36Sopenharmony_ci if (ret < 0) 51262306a36Sopenharmony_ci return ret; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci /* Change the state of external accessory */ 51562306a36Sopenharmony_ci extcon_set_state_sync(info->edev, id, attached); 51662306a36Sopenharmony_ci if (id == EXTCON_USB) 51762306a36Sopenharmony_ci extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SDP, 51862306a36Sopenharmony_ci attached); 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci return 0; 52162306a36Sopenharmony_ci} 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_cistatic void sm5502_muic_irq_work(struct work_struct *work) 52462306a36Sopenharmony_ci{ 52562306a36Sopenharmony_ci struct sm5502_muic_info *info = container_of(work, 52662306a36Sopenharmony_ci struct sm5502_muic_info, irq_work); 52762306a36Sopenharmony_ci int ret = 0; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci if (!info->edev) 53062306a36Sopenharmony_ci return; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci mutex_lock(&info->mutex); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci /* Detect attached or detached cables */ 53562306a36Sopenharmony_ci if (info->irq_attach) { 53662306a36Sopenharmony_ci ret = sm5502_muic_cable_handler(info, true); 53762306a36Sopenharmony_ci info->irq_attach = false; 53862306a36Sopenharmony_ci } 53962306a36Sopenharmony_ci if (info->irq_detach) { 54062306a36Sopenharmony_ci ret = sm5502_muic_cable_handler(info, false); 54162306a36Sopenharmony_ci info->irq_detach = false; 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci if (ret < 0) 54562306a36Sopenharmony_ci dev_err(info->dev, "failed to handle MUIC interrupt\n"); 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci mutex_unlock(&info->mutex); 54862306a36Sopenharmony_ci} 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci/* 55162306a36Sopenharmony_ci * Sets irq_attach or irq_detach in sm5502_muic_info and returns 0. 55262306a36Sopenharmony_ci * Returns -ESRCH if irq_type does not match registered IRQ for this dev type. 55362306a36Sopenharmony_ci */ 55462306a36Sopenharmony_cistatic int sm5502_parse_irq(struct sm5502_muic_info *info, int irq_type) 55562306a36Sopenharmony_ci{ 55662306a36Sopenharmony_ci switch (irq_type) { 55762306a36Sopenharmony_ci case SM5502_IRQ_INT1_ATTACH: 55862306a36Sopenharmony_ci info->irq_attach = true; 55962306a36Sopenharmony_ci break; 56062306a36Sopenharmony_ci case SM5502_IRQ_INT1_DETACH: 56162306a36Sopenharmony_ci info->irq_detach = true; 56262306a36Sopenharmony_ci break; 56362306a36Sopenharmony_ci case SM5502_IRQ_INT1_KP: 56462306a36Sopenharmony_ci case SM5502_IRQ_INT1_LKP: 56562306a36Sopenharmony_ci case SM5502_IRQ_INT1_LKR: 56662306a36Sopenharmony_ci case SM5502_IRQ_INT1_OVP_EVENT: 56762306a36Sopenharmony_ci case SM5502_IRQ_INT1_OCP_EVENT: 56862306a36Sopenharmony_ci case SM5502_IRQ_INT1_OVP_OCP_DIS: 56962306a36Sopenharmony_ci case SM5502_IRQ_INT2_VBUS_DET: 57062306a36Sopenharmony_ci case SM5502_IRQ_INT2_REV_ACCE: 57162306a36Sopenharmony_ci case SM5502_IRQ_INT2_ADC_CHG: 57262306a36Sopenharmony_ci case SM5502_IRQ_INT2_STUCK_KEY: 57362306a36Sopenharmony_ci case SM5502_IRQ_INT2_STUCK_KEY_RCV: 57462306a36Sopenharmony_ci case SM5502_IRQ_INT2_MHL: 57562306a36Sopenharmony_ci default: 57662306a36Sopenharmony_ci break; 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci return 0; 58062306a36Sopenharmony_ci} 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_cistatic int sm5504_parse_irq(struct sm5502_muic_info *info, int irq_type) 58362306a36Sopenharmony_ci{ 58462306a36Sopenharmony_ci switch (irq_type) { 58562306a36Sopenharmony_ci case SM5504_IRQ_INT1_ATTACH: 58662306a36Sopenharmony_ci info->irq_attach = true; 58762306a36Sopenharmony_ci break; 58862306a36Sopenharmony_ci case SM5504_IRQ_INT1_DETACH: 58962306a36Sopenharmony_ci info->irq_detach = true; 59062306a36Sopenharmony_ci break; 59162306a36Sopenharmony_ci case SM5504_IRQ_INT1_CHG_DET: 59262306a36Sopenharmony_ci case SM5504_IRQ_INT1_DCD_OUT: 59362306a36Sopenharmony_ci case SM5504_IRQ_INT1_OVP_EVENT: 59462306a36Sopenharmony_ci case SM5504_IRQ_INT1_CONNECT: 59562306a36Sopenharmony_ci case SM5504_IRQ_INT1_ADC_CHG: 59662306a36Sopenharmony_ci case SM5504_IRQ_INT2_RID_CHG: 59762306a36Sopenharmony_ci case SM5504_IRQ_INT2_UVLO: 59862306a36Sopenharmony_ci case SM5504_IRQ_INT2_POR: 59962306a36Sopenharmony_ci case SM5504_IRQ_INT2_OVP_FET: 60062306a36Sopenharmony_ci case SM5504_IRQ_INT2_OCP_LATCH: 60162306a36Sopenharmony_ci case SM5504_IRQ_INT2_OCP_EVENT: 60262306a36Sopenharmony_ci case SM5504_IRQ_INT2_OVP_OCP_EVENT: 60362306a36Sopenharmony_ci default: 60462306a36Sopenharmony_ci break; 60562306a36Sopenharmony_ci } 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci return 0; 60862306a36Sopenharmony_ci} 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_cistatic irqreturn_t sm5502_muic_irq_handler(int irq, void *data) 61162306a36Sopenharmony_ci{ 61262306a36Sopenharmony_ci struct sm5502_muic_info *info = data; 61362306a36Sopenharmony_ci int i, irq_type = -1, ret; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci for (i = 0; i < info->type->num_muic_irqs; i++) 61662306a36Sopenharmony_ci if (irq == info->type->muic_irqs[i].virq) 61762306a36Sopenharmony_ci irq_type = info->type->muic_irqs[i].irq; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci ret = info->type->parse_irq(info, irq_type); 62062306a36Sopenharmony_ci if (ret < 0) { 62162306a36Sopenharmony_ci dev_warn(info->dev, "cannot handle is interrupt:%d\n", 62262306a36Sopenharmony_ci irq_type); 62362306a36Sopenharmony_ci return IRQ_HANDLED; 62462306a36Sopenharmony_ci } 62562306a36Sopenharmony_ci schedule_work(&info->irq_work); 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci return IRQ_HANDLED; 62862306a36Sopenharmony_ci} 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_cistatic void sm5502_muic_detect_cable_wq(struct work_struct *work) 63162306a36Sopenharmony_ci{ 63262306a36Sopenharmony_ci struct sm5502_muic_info *info = container_of(to_delayed_work(work), 63362306a36Sopenharmony_ci struct sm5502_muic_info, wq_detcable); 63462306a36Sopenharmony_ci int ret; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci /* Notify the state of connector cable or not */ 63762306a36Sopenharmony_ci ret = sm5502_muic_cable_handler(info, true); 63862306a36Sopenharmony_ci if (ret < 0) 63962306a36Sopenharmony_ci dev_warn(info->dev, "failed to detect cable state\n"); 64062306a36Sopenharmony_ci} 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_cistatic void sm5502_init_dev_type(struct sm5502_muic_info *info) 64362306a36Sopenharmony_ci{ 64462306a36Sopenharmony_ci unsigned int reg_data, vendor_id, version_id; 64562306a36Sopenharmony_ci int i, ret; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci /* To test I2C, Print version_id and vendor_id of SM5502 */ 64862306a36Sopenharmony_ci ret = regmap_read(info->regmap, SM5502_REG_DEVICE_ID, ®_data); 64962306a36Sopenharmony_ci if (ret) { 65062306a36Sopenharmony_ci dev_err(info->dev, 65162306a36Sopenharmony_ci "failed to read DEVICE_ID register: %d\n", ret); 65262306a36Sopenharmony_ci return; 65362306a36Sopenharmony_ci } 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci vendor_id = ((reg_data & SM5502_REG_DEVICE_ID_VENDOR_MASK) >> 65662306a36Sopenharmony_ci SM5502_REG_DEVICE_ID_VENDOR_SHIFT); 65762306a36Sopenharmony_ci version_id = ((reg_data & SM5502_REG_DEVICE_ID_VERSION_MASK) >> 65862306a36Sopenharmony_ci SM5502_REG_DEVICE_ID_VERSION_SHIFT); 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci dev_info(info->dev, "Device type: version: 0x%x, vendor: 0x%x\n", 66162306a36Sopenharmony_ci version_id, vendor_id); 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci /* Initiazle the register of SM5502 device to bring-up */ 66462306a36Sopenharmony_ci for (i = 0; i < info->type->num_reg_data; i++) { 66562306a36Sopenharmony_ci unsigned int val = 0; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci if (!info->type->reg_data[i].invert) 66862306a36Sopenharmony_ci val |= ~info->type->reg_data[i].val; 66962306a36Sopenharmony_ci else 67062306a36Sopenharmony_ci val = info->type->reg_data[i].val; 67162306a36Sopenharmony_ci regmap_write(info->regmap, info->type->reg_data[i].reg, val); 67262306a36Sopenharmony_ci } 67362306a36Sopenharmony_ci} 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_cistatic int sm5022_muic_i2c_probe(struct i2c_client *i2c) 67662306a36Sopenharmony_ci{ 67762306a36Sopenharmony_ci struct device_node *np = i2c->dev.of_node; 67862306a36Sopenharmony_ci struct sm5502_muic_info *info; 67962306a36Sopenharmony_ci int i, ret, irq_flags; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci if (!np) 68262306a36Sopenharmony_ci return -EINVAL; 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci info = devm_kzalloc(&i2c->dev, sizeof(*info), GFP_KERNEL); 68562306a36Sopenharmony_ci if (!info) 68662306a36Sopenharmony_ci return -ENOMEM; 68762306a36Sopenharmony_ci i2c_set_clientdata(i2c, info); 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci info->dev = &i2c->dev; 69062306a36Sopenharmony_ci info->i2c = i2c; 69162306a36Sopenharmony_ci info->irq = i2c->irq; 69262306a36Sopenharmony_ci info->type = device_get_match_data(info->dev); 69362306a36Sopenharmony_ci if (!info->type) 69462306a36Sopenharmony_ci return -EINVAL; 69562306a36Sopenharmony_ci if (!info->type->parse_irq) { 69662306a36Sopenharmony_ci dev_err(info->dev, "parse_irq missing in struct sm5502_type\n"); 69762306a36Sopenharmony_ci return -EINVAL; 69862306a36Sopenharmony_ci } 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci mutex_init(&info->mutex); 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci INIT_WORK(&info->irq_work, sm5502_muic_irq_work); 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci info->regmap = devm_regmap_init_i2c(i2c, &sm5502_muic_regmap_config); 70562306a36Sopenharmony_ci if (IS_ERR(info->regmap)) { 70662306a36Sopenharmony_ci ret = PTR_ERR(info->regmap); 70762306a36Sopenharmony_ci dev_err(info->dev, "failed to allocate register map: %d\n", 70862306a36Sopenharmony_ci ret); 70962306a36Sopenharmony_ci return ret; 71062306a36Sopenharmony_ci } 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci /* Support irq domain for SM5502 MUIC device */ 71362306a36Sopenharmony_ci irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT | IRQF_SHARED; 71462306a36Sopenharmony_ci ret = devm_regmap_add_irq_chip(info->dev, info->regmap, info->irq, 71562306a36Sopenharmony_ci irq_flags, 0, info->type->irq_chip, 71662306a36Sopenharmony_ci &info->irq_data); 71762306a36Sopenharmony_ci if (ret != 0) { 71862306a36Sopenharmony_ci dev_err(info->dev, "failed to request IRQ %d: %d\n", 71962306a36Sopenharmony_ci info->irq, ret); 72062306a36Sopenharmony_ci return ret; 72162306a36Sopenharmony_ci } 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci for (i = 0; i < info->type->num_muic_irqs; i++) { 72462306a36Sopenharmony_ci struct muic_irq *muic_irq = &info->type->muic_irqs[i]; 72562306a36Sopenharmony_ci int virq = 0; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci virq = regmap_irq_get_virq(info->irq_data, muic_irq->irq); 72862306a36Sopenharmony_ci if (virq <= 0) 72962306a36Sopenharmony_ci return -EINVAL; 73062306a36Sopenharmony_ci muic_irq->virq = virq; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci ret = devm_request_threaded_irq(info->dev, virq, NULL, 73362306a36Sopenharmony_ci sm5502_muic_irq_handler, 73462306a36Sopenharmony_ci IRQF_NO_SUSPEND | IRQF_ONESHOT, 73562306a36Sopenharmony_ci muic_irq->name, info); 73662306a36Sopenharmony_ci if (ret) { 73762306a36Sopenharmony_ci dev_err(info->dev, 73862306a36Sopenharmony_ci "failed: irq request (IRQ: %d, error :%d)\n", 73962306a36Sopenharmony_ci muic_irq->irq, ret); 74062306a36Sopenharmony_ci return ret; 74162306a36Sopenharmony_ci } 74262306a36Sopenharmony_ci } 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci /* Allocate extcon device */ 74562306a36Sopenharmony_ci info->edev = devm_extcon_dev_allocate(info->dev, sm5502_extcon_cable); 74662306a36Sopenharmony_ci if (IS_ERR(info->edev)) { 74762306a36Sopenharmony_ci dev_err(info->dev, "failed to allocate memory for extcon\n"); 74862306a36Sopenharmony_ci return -ENOMEM; 74962306a36Sopenharmony_ci } 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci /* Register extcon device */ 75262306a36Sopenharmony_ci ret = devm_extcon_dev_register(info->dev, info->edev); 75362306a36Sopenharmony_ci if (ret) { 75462306a36Sopenharmony_ci dev_err(info->dev, "failed to register extcon device\n"); 75562306a36Sopenharmony_ci return ret; 75662306a36Sopenharmony_ci } 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci /* 75962306a36Sopenharmony_ci * Detect accessory after completing the initialization of platform 76062306a36Sopenharmony_ci * 76162306a36Sopenharmony_ci * - Use delayed workqueue to detect cable state and then 76262306a36Sopenharmony_ci * notify cable state to notifiee/platform through uevent. 76362306a36Sopenharmony_ci * After completing the booting of platform, the extcon provider 76462306a36Sopenharmony_ci * driver should notify cable state to upper layer. 76562306a36Sopenharmony_ci */ 76662306a36Sopenharmony_ci INIT_DELAYED_WORK(&info->wq_detcable, sm5502_muic_detect_cable_wq); 76762306a36Sopenharmony_ci queue_delayed_work(system_power_efficient_wq, &info->wq_detcable, 76862306a36Sopenharmony_ci msecs_to_jiffies(DELAY_MS_DEFAULT)); 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci /* Initialize SM5502 device and print vendor id and version id */ 77162306a36Sopenharmony_ci sm5502_init_dev_type(info); 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci return 0; 77462306a36Sopenharmony_ci} 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_cistatic const struct sm5502_type sm5502_data = { 77762306a36Sopenharmony_ci .muic_irqs = sm5502_muic_irqs, 77862306a36Sopenharmony_ci .num_muic_irqs = ARRAY_SIZE(sm5502_muic_irqs), 77962306a36Sopenharmony_ci .irq_chip = &sm5502_muic_irq_chip, 78062306a36Sopenharmony_ci .reg_data = sm5502_reg_data, 78162306a36Sopenharmony_ci .num_reg_data = ARRAY_SIZE(sm5502_reg_data), 78262306a36Sopenharmony_ci .otg_dev_type1 = SM5502_REG_DEV_TYPE1_USB_OTG_MASK, 78362306a36Sopenharmony_ci .parse_irq = sm5502_parse_irq, 78462306a36Sopenharmony_ci}; 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_cistatic const struct sm5502_type sm5504_data = { 78762306a36Sopenharmony_ci .muic_irqs = sm5504_muic_irqs, 78862306a36Sopenharmony_ci .num_muic_irqs = ARRAY_SIZE(sm5504_muic_irqs), 78962306a36Sopenharmony_ci .irq_chip = &sm5504_muic_irq_chip, 79062306a36Sopenharmony_ci .reg_data = sm5504_reg_data, 79162306a36Sopenharmony_ci .num_reg_data = ARRAY_SIZE(sm5504_reg_data), 79262306a36Sopenharmony_ci .otg_dev_type1 = SM5504_REG_DEV_TYPE1_USB_OTG_MASK, 79362306a36Sopenharmony_ci .parse_irq = sm5504_parse_irq, 79462306a36Sopenharmony_ci}; 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_cistatic const struct of_device_id sm5502_dt_match[] = { 79762306a36Sopenharmony_ci { .compatible = "siliconmitus,sm5502-muic", .data = &sm5502_data }, 79862306a36Sopenharmony_ci { .compatible = "siliconmitus,sm5504-muic", .data = &sm5504_data }, 79962306a36Sopenharmony_ci { .compatible = "siliconmitus,sm5703-muic", .data = &sm5502_data }, 80062306a36Sopenharmony_ci { }, 80162306a36Sopenharmony_ci}; 80262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, sm5502_dt_match); 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 80562306a36Sopenharmony_cistatic int sm5502_muic_suspend(struct device *dev) 80662306a36Sopenharmony_ci{ 80762306a36Sopenharmony_ci struct i2c_client *i2c = to_i2c_client(dev); 80862306a36Sopenharmony_ci struct sm5502_muic_info *info = i2c_get_clientdata(i2c); 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci enable_irq_wake(info->irq); 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci return 0; 81362306a36Sopenharmony_ci} 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_cistatic int sm5502_muic_resume(struct device *dev) 81662306a36Sopenharmony_ci{ 81762306a36Sopenharmony_ci struct i2c_client *i2c = to_i2c_client(dev); 81862306a36Sopenharmony_ci struct sm5502_muic_info *info = i2c_get_clientdata(i2c); 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci disable_irq_wake(info->irq); 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci return 0; 82362306a36Sopenharmony_ci} 82462306a36Sopenharmony_ci#endif 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(sm5502_muic_pm_ops, 82762306a36Sopenharmony_ci sm5502_muic_suspend, sm5502_muic_resume); 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_cistatic const struct i2c_device_id sm5502_i2c_id[] = { 83062306a36Sopenharmony_ci { "sm5502", (kernel_ulong_t)&sm5502_data }, 83162306a36Sopenharmony_ci { "sm5504", (kernel_ulong_t)&sm5504_data }, 83262306a36Sopenharmony_ci { "sm5703-muic", (kernel_ulong_t)&sm5502_data }, 83362306a36Sopenharmony_ci { } 83462306a36Sopenharmony_ci}; 83562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, sm5502_i2c_id); 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_cistatic struct i2c_driver sm5502_muic_i2c_driver = { 83862306a36Sopenharmony_ci .driver = { 83962306a36Sopenharmony_ci .name = "sm5502", 84062306a36Sopenharmony_ci .pm = &sm5502_muic_pm_ops, 84162306a36Sopenharmony_ci .of_match_table = sm5502_dt_match, 84262306a36Sopenharmony_ci }, 84362306a36Sopenharmony_ci .probe = sm5022_muic_i2c_probe, 84462306a36Sopenharmony_ci .id_table = sm5502_i2c_id, 84562306a36Sopenharmony_ci}; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_cistatic int __init sm5502_muic_i2c_init(void) 84862306a36Sopenharmony_ci{ 84962306a36Sopenharmony_ci return i2c_add_driver(&sm5502_muic_i2c_driver); 85062306a36Sopenharmony_ci} 85162306a36Sopenharmony_cisubsys_initcall(sm5502_muic_i2c_init); 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ciMODULE_DESCRIPTION("Silicon Mitus SM5502 Extcon driver"); 85462306a36Sopenharmony_ciMODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>"); 85562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 856