162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// extcon-max14577.c - MAX14577/77836 extcon driver to support MUIC 462306a36Sopenharmony_ci// 562306a36Sopenharmony_ci// Copyright (C) 2013,2014 Samsung Electronics 662306a36Sopenharmony_ci// Chanwoo Choi <cw00.choi@samsung.com> 762306a36Sopenharmony_ci// Krzysztof Kozlowski <krzk@kernel.org> 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/devm-helpers.h> 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/i2c.h> 1362306a36Sopenharmony_ci#include <linux/interrupt.h> 1462306a36Sopenharmony_ci#include <linux/platform_device.h> 1562306a36Sopenharmony_ci#include <linux/mfd/max14577.h> 1662306a36Sopenharmony_ci#include <linux/mfd/max14577-private.h> 1762306a36Sopenharmony_ci#include <linux/extcon-provider.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define DELAY_MS_DEFAULT 17000 /* unit: millisecond */ 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cienum max14577_muic_adc_debounce_time { 2262306a36Sopenharmony_ci ADC_DEBOUNCE_TIME_5MS = 0, 2362306a36Sopenharmony_ci ADC_DEBOUNCE_TIME_10MS, 2462306a36Sopenharmony_ci ADC_DEBOUNCE_TIME_25MS, 2562306a36Sopenharmony_ci ADC_DEBOUNCE_TIME_38_62MS, 2662306a36Sopenharmony_ci}; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cienum max14577_muic_status { 2962306a36Sopenharmony_ci MAX14577_MUIC_STATUS1 = 0, 3062306a36Sopenharmony_ci MAX14577_MUIC_STATUS2 = 1, 3162306a36Sopenharmony_ci MAX14577_MUIC_STATUS_END, 3262306a36Sopenharmony_ci}; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/** 3562306a36Sopenharmony_ci * struct max14577_muic_irq 3662306a36Sopenharmony_ci * @irq: the index of irq list of MUIC device. 3762306a36Sopenharmony_ci * @name: the name of irq. 3862306a36Sopenharmony_ci * @virq: the virtual irq to use irq domain 3962306a36Sopenharmony_ci */ 4062306a36Sopenharmony_cistruct max14577_muic_irq { 4162306a36Sopenharmony_ci unsigned int irq; 4262306a36Sopenharmony_ci const char *name; 4362306a36Sopenharmony_ci unsigned int virq; 4462306a36Sopenharmony_ci}; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic struct max14577_muic_irq max14577_muic_irqs[] = { 4762306a36Sopenharmony_ci { MAX14577_IRQ_INT1_ADC, "muic-ADC" }, 4862306a36Sopenharmony_ci { MAX14577_IRQ_INT1_ADCLOW, "muic-ADCLOW" }, 4962306a36Sopenharmony_ci { MAX14577_IRQ_INT1_ADCERR, "muic-ADCError" }, 5062306a36Sopenharmony_ci { MAX14577_IRQ_INT2_CHGTYP, "muic-CHGTYP" }, 5162306a36Sopenharmony_ci { MAX14577_IRQ_INT2_CHGDETRUN, "muic-CHGDETRUN" }, 5262306a36Sopenharmony_ci { MAX14577_IRQ_INT2_DCDTMR, "muic-DCDTMR" }, 5362306a36Sopenharmony_ci { MAX14577_IRQ_INT2_DBCHG, "muic-DBCHG" }, 5462306a36Sopenharmony_ci { MAX14577_IRQ_INT2_VBVOLT, "muic-VBVOLT" }, 5562306a36Sopenharmony_ci}; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic struct max14577_muic_irq max77836_muic_irqs[] = { 5862306a36Sopenharmony_ci { MAX14577_IRQ_INT1_ADC, "muic-ADC" }, 5962306a36Sopenharmony_ci { MAX14577_IRQ_INT1_ADCLOW, "muic-ADCLOW" }, 6062306a36Sopenharmony_ci { MAX14577_IRQ_INT1_ADCERR, "muic-ADCError" }, 6162306a36Sopenharmony_ci { MAX77836_IRQ_INT1_ADC1K, "muic-ADC1K" }, 6262306a36Sopenharmony_ci { MAX14577_IRQ_INT2_CHGTYP, "muic-CHGTYP" }, 6362306a36Sopenharmony_ci { MAX14577_IRQ_INT2_CHGDETRUN, "muic-CHGDETRUN" }, 6462306a36Sopenharmony_ci { MAX14577_IRQ_INT2_DCDTMR, "muic-DCDTMR" }, 6562306a36Sopenharmony_ci { MAX14577_IRQ_INT2_DBCHG, "muic-DBCHG" }, 6662306a36Sopenharmony_ci { MAX14577_IRQ_INT2_VBVOLT, "muic-VBVOLT" }, 6762306a36Sopenharmony_ci { MAX77836_IRQ_INT2_VIDRM, "muic-VIDRM" }, 6862306a36Sopenharmony_ci}; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistruct max14577_muic_info { 7162306a36Sopenharmony_ci struct device *dev; 7262306a36Sopenharmony_ci struct max14577 *max14577; 7362306a36Sopenharmony_ci struct extcon_dev *edev; 7462306a36Sopenharmony_ci int prev_cable_type; 7562306a36Sopenharmony_ci int prev_chg_type; 7662306a36Sopenharmony_ci u8 status[MAX14577_MUIC_STATUS_END]; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci struct max14577_muic_irq *muic_irqs; 7962306a36Sopenharmony_ci unsigned int muic_irqs_num; 8062306a36Sopenharmony_ci bool irq_adc; 8162306a36Sopenharmony_ci bool irq_chg; 8262306a36Sopenharmony_ci struct work_struct irq_work; 8362306a36Sopenharmony_ci struct mutex mutex; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci /* 8662306a36Sopenharmony_ci * Use delayed workqueue to detect cable state and then 8762306a36Sopenharmony_ci * notify cable state to notifiee/platform through uevent. 8862306a36Sopenharmony_ci * After completing the booting of platform, the extcon provider 8962306a36Sopenharmony_ci * driver should notify cable state to upper layer. 9062306a36Sopenharmony_ci */ 9162306a36Sopenharmony_ci struct delayed_work wq_detcable; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci /* 9462306a36Sopenharmony_ci * Default usb/uart path whether UART/USB or AUX_UART/AUX_USB 9562306a36Sopenharmony_ci * h/w path of COMP2/COMN1 on CONTROL1 register. 9662306a36Sopenharmony_ci */ 9762306a36Sopenharmony_ci int path_usb; 9862306a36Sopenharmony_ci int path_uart; 9962306a36Sopenharmony_ci}; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cienum max14577_muic_cable_group { 10262306a36Sopenharmony_ci MAX14577_CABLE_GROUP_ADC = 0, 10362306a36Sopenharmony_ci MAX14577_CABLE_GROUP_CHG, 10462306a36Sopenharmony_ci}; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci/* Define supported accessory type */ 10762306a36Sopenharmony_cienum max14577_muic_acc_type { 10862306a36Sopenharmony_ci MAX14577_MUIC_ADC_GROUND = 0x0, 10962306a36Sopenharmony_ci MAX14577_MUIC_ADC_SEND_END_BUTTON, 11062306a36Sopenharmony_ci MAX14577_MUIC_ADC_REMOTE_S1_BUTTON, 11162306a36Sopenharmony_ci MAX14577_MUIC_ADC_REMOTE_S2_BUTTON, 11262306a36Sopenharmony_ci MAX14577_MUIC_ADC_REMOTE_S3_BUTTON, 11362306a36Sopenharmony_ci MAX14577_MUIC_ADC_REMOTE_S4_BUTTON, 11462306a36Sopenharmony_ci MAX14577_MUIC_ADC_REMOTE_S5_BUTTON, 11562306a36Sopenharmony_ci MAX14577_MUIC_ADC_REMOTE_S6_BUTTON, 11662306a36Sopenharmony_ci MAX14577_MUIC_ADC_REMOTE_S7_BUTTON, 11762306a36Sopenharmony_ci MAX14577_MUIC_ADC_REMOTE_S8_BUTTON, 11862306a36Sopenharmony_ci MAX14577_MUIC_ADC_REMOTE_S9_BUTTON, 11962306a36Sopenharmony_ci MAX14577_MUIC_ADC_REMOTE_S10_BUTTON, 12062306a36Sopenharmony_ci MAX14577_MUIC_ADC_REMOTE_S11_BUTTON, 12162306a36Sopenharmony_ci MAX14577_MUIC_ADC_REMOTE_S12_BUTTON, 12262306a36Sopenharmony_ci MAX14577_MUIC_ADC_RESERVED_ACC_1, 12362306a36Sopenharmony_ci MAX14577_MUIC_ADC_RESERVED_ACC_2, 12462306a36Sopenharmony_ci MAX14577_MUIC_ADC_RESERVED_ACC_3, 12562306a36Sopenharmony_ci MAX14577_MUIC_ADC_RESERVED_ACC_4, 12662306a36Sopenharmony_ci MAX14577_MUIC_ADC_RESERVED_ACC_5, 12762306a36Sopenharmony_ci MAX14577_MUIC_ADC_AUDIO_DEVICE_TYPE2, 12862306a36Sopenharmony_ci MAX14577_MUIC_ADC_PHONE_POWERED_DEV, 12962306a36Sopenharmony_ci MAX14577_MUIC_ADC_TTY_CONVERTER, 13062306a36Sopenharmony_ci MAX14577_MUIC_ADC_UART_CABLE, 13162306a36Sopenharmony_ci MAX14577_MUIC_ADC_CEA936A_TYPE1_CHG, 13262306a36Sopenharmony_ci MAX14577_MUIC_ADC_FACTORY_MODE_USB_OFF, 13362306a36Sopenharmony_ci MAX14577_MUIC_ADC_FACTORY_MODE_USB_ON, 13462306a36Sopenharmony_ci MAX14577_MUIC_ADC_AV_CABLE_NOLOAD, 13562306a36Sopenharmony_ci MAX14577_MUIC_ADC_CEA936A_TYPE2_CHG, 13662306a36Sopenharmony_ci MAX14577_MUIC_ADC_FACTORY_MODE_UART_OFF, 13762306a36Sopenharmony_ci MAX14577_MUIC_ADC_FACTORY_MODE_UART_ON, 13862306a36Sopenharmony_ci MAX14577_MUIC_ADC_AUDIO_DEVICE_TYPE1, /* with Remote and Simple Ctrl */ 13962306a36Sopenharmony_ci MAX14577_MUIC_ADC_OPEN, 14062306a36Sopenharmony_ci}; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic const unsigned int max14577_extcon_cable[] = { 14362306a36Sopenharmony_ci EXTCON_USB, 14462306a36Sopenharmony_ci EXTCON_CHG_USB_SDP, 14562306a36Sopenharmony_ci EXTCON_CHG_USB_DCP, 14662306a36Sopenharmony_ci EXTCON_CHG_USB_FAST, 14762306a36Sopenharmony_ci EXTCON_CHG_USB_SLOW, 14862306a36Sopenharmony_ci EXTCON_CHG_USB_CDP, 14962306a36Sopenharmony_ci EXTCON_JIG, 15062306a36Sopenharmony_ci EXTCON_NONE, 15162306a36Sopenharmony_ci}; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci/* 15462306a36Sopenharmony_ci * max14577_muic_set_debounce_time - Set the debounce time of ADC 15562306a36Sopenharmony_ci * @info: the instance including private data of max14577 MUIC 15662306a36Sopenharmony_ci * @time: the debounce time of ADC 15762306a36Sopenharmony_ci */ 15862306a36Sopenharmony_cistatic int max14577_muic_set_debounce_time(struct max14577_muic_info *info, 15962306a36Sopenharmony_ci enum max14577_muic_adc_debounce_time time) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci u8 ret; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci switch (time) { 16462306a36Sopenharmony_ci case ADC_DEBOUNCE_TIME_5MS: 16562306a36Sopenharmony_ci case ADC_DEBOUNCE_TIME_10MS: 16662306a36Sopenharmony_ci case ADC_DEBOUNCE_TIME_25MS: 16762306a36Sopenharmony_ci case ADC_DEBOUNCE_TIME_38_62MS: 16862306a36Sopenharmony_ci ret = max14577_update_reg(info->max14577->regmap, 16962306a36Sopenharmony_ci MAX14577_MUIC_REG_CONTROL3, 17062306a36Sopenharmony_ci CTRL3_ADCDBSET_MASK, 17162306a36Sopenharmony_ci time << CTRL3_ADCDBSET_SHIFT); 17262306a36Sopenharmony_ci if (ret) { 17362306a36Sopenharmony_ci dev_err(info->dev, "failed to set ADC debounce time\n"); 17462306a36Sopenharmony_ci return ret; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci break; 17762306a36Sopenharmony_ci default: 17862306a36Sopenharmony_ci dev_err(info->dev, "invalid ADC debounce time\n"); 17962306a36Sopenharmony_ci return -EINVAL; 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci return 0; 18362306a36Sopenharmony_ci}; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci/* 18662306a36Sopenharmony_ci * max14577_muic_set_path - Set hardware line according to attached cable 18762306a36Sopenharmony_ci * @info: the instance including private data of max14577 MUIC 18862306a36Sopenharmony_ci * @value: the path according to attached cable 18962306a36Sopenharmony_ci * @attached: the state of cable (true:attached, false:detached) 19062306a36Sopenharmony_ci * 19162306a36Sopenharmony_ci * The max14577 MUIC device share outside H/W line among a varity of cables 19262306a36Sopenharmony_ci * so, this function set internal path of H/W line according to the type of 19362306a36Sopenharmony_ci * attached cable. 19462306a36Sopenharmony_ci */ 19562306a36Sopenharmony_cistatic int max14577_muic_set_path(struct max14577_muic_info *info, 19662306a36Sopenharmony_ci u8 val, bool attached) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci u8 ctrl1, ctrl2 = 0; 19962306a36Sopenharmony_ci int ret; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci /* Set open state to path before changing hw path */ 20262306a36Sopenharmony_ci ret = max14577_update_reg(info->max14577->regmap, 20362306a36Sopenharmony_ci MAX14577_MUIC_REG_CONTROL1, 20462306a36Sopenharmony_ci CLEAR_IDBEN_MICEN_MASK, CTRL1_SW_OPEN); 20562306a36Sopenharmony_ci if (ret < 0) { 20662306a36Sopenharmony_ci dev_err(info->dev, "failed to update MUIC register\n"); 20762306a36Sopenharmony_ci return ret; 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci if (attached) 21162306a36Sopenharmony_ci ctrl1 = val; 21262306a36Sopenharmony_ci else 21362306a36Sopenharmony_ci ctrl1 = CTRL1_SW_OPEN; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci ret = max14577_update_reg(info->max14577->regmap, 21662306a36Sopenharmony_ci MAX14577_MUIC_REG_CONTROL1, 21762306a36Sopenharmony_ci CLEAR_IDBEN_MICEN_MASK, ctrl1); 21862306a36Sopenharmony_ci if (ret < 0) { 21962306a36Sopenharmony_ci dev_err(info->dev, "failed to update MUIC register\n"); 22062306a36Sopenharmony_ci return ret; 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci if (attached) 22462306a36Sopenharmony_ci ctrl2 |= CTRL2_CPEN_MASK; /* LowPwr=0, CPEn=1 */ 22562306a36Sopenharmony_ci else 22662306a36Sopenharmony_ci ctrl2 |= CTRL2_LOWPWR_MASK; /* LowPwr=1, CPEn=0 */ 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci ret = max14577_update_reg(info->max14577->regmap, 22962306a36Sopenharmony_ci MAX14577_REG_CONTROL2, 23062306a36Sopenharmony_ci CTRL2_LOWPWR_MASK | CTRL2_CPEN_MASK, ctrl2); 23162306a36Sopenharmony_ci if (ret < 0) { 23262306a36Sopenharmony_ci dev_err(info->dev, "failed to update MUIC register\n"); 23362306a36Sopenharmony_ci return ret; 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci dev_dbg(info->dev, 23762306a36Sopenharmony_ci "CONTROL1 : 0x%02x, CONTROL2 : 0x%02x, state : %s\n", 23862306a36Sopenharmony_ci ctrl1, ctrl2, attached ? "attached" : "detached"); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci return 0; 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci/* 24462306a36Sopenharmony_ci * max14577_muic_get_cable_type - Return cable type and check cable state 24562306a36Sopenharmony_ci * @info: the instance including private data of max14577 MUIC 24662306a36Sopenharmony_ci * @group: the path according to attached cable 24762306a36Sopenharmony_ci * @attached: store cable state and return 24862306a36Sopenharmony_ci * 24962306a36Sopenharmony_ci * This function check the cable state either attached or detached, 25062306a36Sopenharmony_ci * and then divide precise type of cable according to cable group. 25162306a36Sopenharmony_ci * - max14577_CABLE_GROUP_ADC 25262306a36Sopenharmony_ci * - max14577_CABLE_GROUP_CHG 25362306a36Sopenharmony_ci */ 25462306a36Sopenharmony_cistatic int max14577_muic_get_cable_type(struct max14577_muic_info *info, 25562306a36Sopenharmony_ci enum max14577_muic_cable_group group, bool *attached) 25662306a36Sopenharmony_ci{ 25762306a36Sopenharmony_ci int cable_type = 0; 25862306a36Sopenharmony_ci int adc; 25962306a36Sopenharmony_ci int chg_type; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci switch (group) { 26262306a36Sopenharmony_ci case MAX14577_CABLE_GROUP_ADC: 26362306a36Sopenharmony_ci /* 26462306a36Sopenharmony_ci * Read ADC value to check cable type and decide cable state 26562306a36Sopenharmony_ci * according to cable type 26662306a36Sopenharmony_ci */ 26762306a36Sopenharmony_ci adc = info->status[MAX14577_MUIC_STATUS1] & STATUS1_ADC_MASK; 26862306a36Sopenharmony_ci adc >>= STATUS1_ADC_SHIFT; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci /* 27162306a36Sopenharmony_ci * Check current cable state/cable type and store cable type 27262306a36Sopenharmony_ci * (info->prev_cable_type) for handling cable when cable is 27362306a36Sopenharmony_ci * detached. 27462306a36Sopenharmony_ci */ 27562306a36Sopenharmony_ci if (adc == MAX14577_MUIC_ADC_OPEN) { 27662306a36Sopenharmony_ci *attached = false; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci cable_type = info->prev_cable_type; 27962306a36Sopenharmony_ci info->prev_cable_type = MAX14577_MUIC_ADC_OPEN; 28062306a36Sopenharmony_ci } else { 28162306a36Sopenharmony_ci *attached = true; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci cable_type = info->prev_cable_type = adc; 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci break; 28662306a36Sopenharmony_ci case MAX14577_CABLE_GROUP_CHG: 28762306a36Sopenharmony_ci /* 28862306a36Sopenharmony_ci * Read charger type to check cable type and decide cable state 28962306a36Sopenharmony_ci * according to type of charger cable. 29062306a36Sopenharmony_ci */ 29162306a36Sopenharmony_ci chg_type = info->status[MAX14577_MUIC_STATUS2] & 29262306a36Sopenharmony_ci STATUS2_CHGTYP_MASK; 29362306a36Sopenharmony_ci chg_type >>= STATUS2_CHGTYP_SHIFT; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci if (chg_type == MAX14577_CHARGER_TYPE_NONE) { 29662306a36Sopenharmony_ci *attached = false; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci cable_type = info->prev_chg_type; 29962306a36Sopenharmony_ci info->prev_chg_type = MAX14577_CHARGER_TYPE_NONE; 30062306a36Sopenharmony_ci } else { 30162306a36Sopenharmony_ci *attached = true; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci /* 30462306a36Sopenharmony_ci * Check current cable state/cable type and store cable 30562306a36Sopenharmony_ci * type(info->prev_chg_type) for handling cable when 30662306a36Sopenharmony_ci * charger cable is detached. 30762306a36Sopenharmony_ci */ 30862306a36Sopenharmony_ci cable_type = info->prev_chg_type = chg_type; 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci break; 31262306a36Sopenharmony_ci default: 31362306a36Sopenharmony_ci dev_err(info->dev, "Unknown cable group (%d)\n", group); 31462306a36Sopenharmony_ci cable_type = -EINVAL; 31562306a36Sopenharmony_ci break; 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci return cable_type; 31962306a36Sopenharmony_ci} 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_cistatic int max14577_muic_jig_handler(struct max14577_muic_info *info, 32262306a36Sopenharmony_ci int cable_type, bool attached) 32362306a36Sopenharmony_ci{ 32462306a36Sopenharmony_ci int ret = 0; 32562306a36Sopenharmony_ci u8 path = CTRL1_SW_OPEN; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci dev_dbg(info->dev, 32862306a36Sopenharmony_ci "external connector is %s (adc:0x%02x)\n", 32962306a36Sopenharmony_ci attached ? "attached" : "detached", cable_type); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci switch (cable_type) { 33262306a36Sopenharmony_ci case MAX14577_MUIC_ADC_FACTORY_MODE_USB_OFF: /* ADC_JIG_USB_OFF */ 33362306a36Sopenharmony_ci case MAX14577_MUIC_ADC_FACTORY_MODE_USB_ON: /* ADC_JIG_USB_ON */ 33462306a36Sopenharmony_ci /* PATH:AP_USB */ 33562306a36Sopenharmony_ci path = CTRL1_SW_USB; 33662306a36Sopenharmony_ci break; 33762306a36Sopenharmony_ci case MAX14577_MUIC_ADC_FACTORY_MODE_UART_OFF: /* ADC_JIG_UART_OFF */ 33862306a36Sopenharmony_ci /* PATH:AP_UART */ 33962306a36Sopenharmony_ci path = CTRL1_SW_UART; 34062306a36Sopenharmony_ci break; 34162306a36Sopenharmony_ci default: 34262306a36Sopenharmony_ci dev_err(info->dev, "failed to detect %s jig cable\n", 34362306a36Sopenharmony_ci attached ? "attached" : "detached"); 34462306a36Sopenharmony_ci return -EINVAL; 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci ret = max14577_muic_set_path(info, path, attached); 34862306a36Sopenharmony_ci if (ret < 0) 34962306a36Sopenharmony_ci return ret; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci extcon_set_state_sync(info->edev, EXTCON_JIG, attached); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci return 0; 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cistatic int max14577_muic_adc_handler(struct max14577_muic_info *info) 35762306a36Sopenharmony_ci{ 35862306a36Sopenharmony_ci int cable_type; 35962306a36Sopenharmony_ci bool attached; 36062306a36Sopenharmony_ci int ret = 0; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci /* Check accessory state which is either detached or attached */ 36362306a36Sopenharmony_ci cable_type = max14577_muic_get_cable_type(info, 36462306a36Sopenharmony_ci MAX14577_CABLE_GROUP_ADC, &attached); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci dev_dbg(info->dev, 36762306a36Sopenharmony_ci "external connector is %s (adc:0x%02x, prev_adc:0x%x)\n", 36862306a36Sopenharmony_ci attached ? "attached" : "detached", cable_type, 36962306a36Sopenharmony_ci info->prev_cable_type); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci switch (cable_type) { 37262306a36Sopenharmony_ci case MAX14577_MUIC_ADC_FACTORY_MODE_USB_OFF: 37362306a36Sopenharmony_ci case MAX14577_MUIC_ADC_FACTORY_MODE_USB_ON: 37462306a36Sopenharmony_ci case MAX14577_MUIC_ADC_FACTORY_MODE_UART_OFF: 37562306a36Sopenharmony_ci /* JIG */ 37662306a36Sopenharmony_ci ret = max14577_muic_jig_handler(info, cable_type, attached); 37762306a36Sopenharmony_ci if (ret < 0) 37862306a36Sopenharmony_ci return ret; 37962306a36Sopenharmony_ci break; 38062306a36Sopenharmony_ci case MAX14577_MUIC_ADC_GROUND: 38162306a36Sopenharmony_ci case MAX14577_MUIC_ADC_SEND_END_BUTTON: 38262306a36Sopenharmony_ci case MAX14577_MUIC_ADC_REMOTE_S1_BUTTON: 38362306a36Sopenharmony_ci case MAX14577_MUIC_ADC_REMOTE_S2_BUTTON: 38462306a36Sopenharmony_ci case MAX14577_MUIC_ADC_REMOTE_S3_BUTTON: 38562306a36Sopenharmony_ci case MAX14577_MUIC_ADC_REMOTE_S4_BUTTON: 38662306a36Sopenharmony_ci case MAX14577_MUIC_ADC_REMOTE_S5_BUTTON: 38762306a36Sopenharmony_ci case MAX14577_MUIC_ADC_REMOTE_S6_BUTTON: 38862306a36Sopenharmony_ci case MAX14577_MUIC_ADC_REMOTE_S7_BUTTON: 38962306a36Sopenharmony_ci case MAX14577_MUIC_ADC_REMOTE_S8_BUTTON: 39062306a36Sopenharmony_ci case MAX14577_MUIC_ADC_REMOTE_S9_BUTTON: 39162306a36Sopenharmony_ci case MAX14577_MUIC_ADC_REMOTE_S10_BUTTON: 39262306a36Sopenharmony_ci case MAX14577_MUIC_ADC_REMOTE_S11_BUTTON: 39362306a36Sopenharmony_ci case MAX14577_MUIC_ADC_REMOTE_S12_BUTTON: 39462306a36Sopenharmony_ci case MAX14577_MUIC_ADC_RESERVED_ACC_1: 39562306a36Sopenharmony_ci case MAX14577_MUIC_ADC_RESERVED_ACC_2: 39662306a36Sopenharmony_ci case MAX14577_MUIC_ADC_RESERVED_ACC_3: 39762306a36Sopenharmony_ci case MAX14577_MUIC_ADC_RESERVED_ACC_4: 39862306a36Sopenharmony_ci case MAX14577_MUIC_ADC_RESERVED_ACC_5: 39962306a36Sopenharmony_ci case MAX14577_MUIC_ADC_AUDIO_DEVICE_TYPE2: 40062306a36Sopenharmony_ci case MAX14577_MUIC_ADC_PHONE_POWERED_DEV: 40162306a36Sopenharmony_ci case MAX14577_MUIC_ADC_TTY_CONVERTER: 40262306a36Sopenharmony_ci case MAX14577_MUIC_ADC_UART_CABLE: 40362306a36Sopenharmony_ci case MAX14577_MUIC_ADC_CEA936A_TYPE1_CHG: 40462306a36Sopenharmony_ci case MAX14577_MUIC_ADC_AV_CABLE_NOLOAD: 40562306a36Sopenharmony_ci case MAX14577_MUIC_ADC_CEA936A_TYPE2_CHG: 40662306a36Sopenharmony_ci case MAX14577_MUIC_ADC_FACTORY_MODE_UART_ON: 40762306a36Sopenharmony_ci case MAX14577_MUIC_ADC_AUDIO_DEVICE_TYPE1: 40862306a36Sopenharmony_ci /* 40962306a36Sopenharmony_ci * This accessory isn't used in general case if it is specially 41062306a36Sopenharmony_ci * needed to detect additional accessory, should implement 41162306a36Sopenharmony_ci * proper operation when this accessory is attached/detached. 41262306a36Sopenharmony_ci */ 41362306a36Sopenharmony_ci dev_info(info->dev, 41462306a36Sopenharmony_ci "accessory is %s but it isn't used (adc:0x%x)\n", 41562306a36Sopenharmony_ci attached ? "attached" : "detached", cable_type); 41662306a36Sopenharmony_ci return -EAGAIN; 41762306a36Sopenharmony_ci default: 41862306a36Sopenharmony_ci dev_err(info->dev, 41962306a36Sopenharmony_ci "failed to detect %s accessory (adc:0x%x)\n", 42062306a36Sopenharmony_ci attached ? "attached" : "detached", cable_type); 42162306a36Sopenharmony_ci return -EINVAL; 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci return 0; 42562306a36Sopenharmony_ci} 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_cistatic int max14577_muic_chg_handler(struct max14577_muic_info *info) 42862306a36Sopenharmony_ci{ 42962306a36Sopenharmony_ci int chg_type; 43062306a36Sopenharmony_ci bool attached; 43162306a36Sopenharmony_ci int ret = 0; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci chg_type = max14577_muic_get_cable_type(info, 43462306a36Sopenharmony_ci MAX14577_CABLE_GROUP_CHG, &attached); 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci dev_dbg(info->dev, 43762306a36Sopenharmony_ci "external connector is %s(chg_type:0x%x, prev_chg_type:0x%x)\n", 43862306a36Sopenharmony_ci attached ? "attached" : "detached", 43962306a36Sopenharmony_ci chg_type, info->prev_chg_type); 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci switch (chg_type) { 44262306a36Sopenharmony_ci case MAX14577_CHARGER_TYPE_USB: 44362306a36Sopenharmony_ci /* PATH:AP_USB */ 44462306a36Sopenharmony_ci ret = max14577_muic_set_path(info, info->path_usb, attached); 44562306a36Sopenharmony_ci if (ret < 0) 44662306a36Sopenharmony_ci return ret; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci extcon_set_state_sync(info->edev, EXTCON_USB, attached); 44962306a36Sopenharmony_ci extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SDP, 45062306a36Sopenharmony_ci attached); 45162306a36Sopenharmony_ci break; 45262306a36Sopenharmony_ci case MAX14577_CHARGER_TYPE_DEDICATED_CHG: 45362306a36Sopenharmony_ci extcon_set_state_sync(info->edev, EXTCON_CHG_USB_DCP, 45462306a36Sopenharmony_ci attached); 45562306a36Sopenharmony_ci break; 45662306a36Sopenharmony_ci case MAX14577_CHARGER_TYPE_DOWNSTREAM_PORT: 45762306a36Sopenharmony_ci extcon_set_state_sync(info->edev, EXTCON_CHG_USB_CDP, 45862306a36Sopenharmony_ci attached); 45962306a36Sopenharmony_ci break; 46062306a36Sopenharmony_ci case MAX14577_CHARGER_TYPE_SPECIAL_500MA: 46162306a36Sopenharmony_ci extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SLOW, 46262306a36Sopenharmony_ci attached); 46362306a36Sopenharmony_ci break; 46462306a36Sopenharmony_ci case MAX14577_CHARGER_TYPE_SPECIAL_1A: 46562306a36Sopenharmony_ci extcon_set_state_sync(info->edev, EXTCON_CHG_USB_FAST, 46662306a36Sopenharmony_ci attached); 46762306a36Sopenharmony_ci break; 46862306a36Sopenharmony_ci case MAX14577_CHARGER_TYPE_NONE: 46962306a36Sopenharmony_ci case MAX14577_CHARGER_TYPE_DEAD_BATTERY: 47062306a36Sopenharmony_ci break; 47162306a36Sopenharmony_ci default: 47262306a36Sopenharmony_ci dev_err(info->dev, 47362306a36Sopenharmony_ci "failed to detect %s accessory (chg_type:0x%x)\n", 47462306a36Sopenharmony_ci attached ? "attached" : "detached", chg_type); 47562306a36Sopenharmony_ci return -EINVAL; 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci return 0; 47962306a36Sopenharmony_ci} 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_cistatic void max14577_muic_irq_work(struct work_struct *work) 48262306a36Sopenharmony_ci{ 48362306a36Sopenharmony_ci struct max14577_muic_info *info = container_of(work, 48462306a36Sopenharmony_ci struct max14577_muic_info, irq_work); 48562306a36Sopenharmony_ci int ret = 0; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci if (!info->edev) 48862306a36Sopenharmony_ci return; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci mutex_lock(&info->mutex); 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci ret = max14577_bulk_read(info->max14577->regmap, 49362306a36Sopenharmony_ci MAX14577_MUIC_REG_STATUS1, info->status, 2); 49462306a36Sopenharmony_ci if (ret) { 49562306a36Sopenharmony_ci dev_err(info->dev, "failed to read MUIC register\n"); 49662306a36Sopenharmony_ci mutex_unlock(&info->mutex); 49762306a36Sopenharmony_ci return; 49862306a36Sopenharmony_ci } 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci if (info->irq_adc) { 50162306a36Sopenharmony_ci ret = max14577_muic_adc_handler(info); 50262306a36Sopenharmony_ci info->irq_adc = false; 50362306a36Sopenharmony_ci } 50462306a36Sopenharmony_ci if (info->irq_chg) { 50562306a36Sopenharmony_ci ret = max14577_muic_chg_handler(info); 50662306a36Sopenharmony_ci info->irq_chg = false; 50762306a36Sopenharmony_ci } 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci if (ret < 0) 51062306a36Sopenharmony_ci dev_err(info->dev, "failed to handle MUIC interrupt\n"); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci mutex_unlock(&info->mutex); 51362306a36Sopenharmony_ci} 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci/* 51662306a36Sopenharmony_ci * Sets irq_adc or irq_chg in max14577_muic_info and returns 1. 51762306a36Sopenharmony_ci * Returns 0 if irq_type does not match registered IRQ for this device type. 51862306a36Sopenharmony_ci */ 51962306a36Sopenharmony_cistatic int max14577_parse_irq(struct max14577_muic_info *info, int irq_type) 52062306a36Sopenharmony_ci{ 52162306a36Sopenharmony_ci switch (irq_type) { 52262306a36Sopenharmony_ci case MAX14577_IRQ_INT1_ADC: 52362306a36Sopenharmony_ci case MAX14577_IRQ_INT1_ADCLOW: 52462306a36Sopenharmony_ci case MAX14577_IRQ_INT1_ADCERR: 52562306a36Sopenharmony_ci /* 52662306a36Sopenharmony_ci * Handle all of accessory except for 52762306a36Sopenharmony_ci * type of charger accessory. 52862306a36Sopenharmony_ci */ 52962306a36Sopenharmony_ci info->irq_adc = true; 53062306a36Sopenharmony_ci return 1; 53162306a36Sopenharmony_ci case MAX14577_IRQ_INT2_CHGTYP: 53262306a36Sopenharmony_ci case MAX14577_IRQ_INT2_CHGDETRUN: 53362306a36Sopenharmony_ci case MAX14577_IRQ_INT2_DCDTMR: 53462306a36Sopenharmony_ci case MAX14577_IRQ_INT2_DBCHG: 53562306a36Sopenharmony_ci case MAX14577_IRQ_INT2_VBVOLT: 53662306a36Sopenharmony_ci /* Handle charger accessory */ 53762306a36Sopenharmony_ci info->irq_chg = true; 53862306a36Sopenharmony_ci return 1; 53962306a36Sopenharmony_ci default: 54062306a36Sopenharmony_ci return 0; 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci} 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci/* 54562306a36Sopenharmony_ci * Sets irq_adc or irq_chg in max14577_muic_info and returns 1. 54662306a36Sopenharmony_ci * Returns 0 if irq_type does not match registered IRQ for this device type. 54762306a36Sopenharmony_ci */ 54862306a36Sopenharmony_cistatic int max77836_parse_irq(struct max14577_muic_info *info, int irq_type) 54962306a36Sopenharmony_ci{ 55062306a36Sopenharmony_ci /* First check common max14577 interrupts */ 55162306a36Sopenharmony_ci if (max14577_parse_irq(info, irq_type)) 55262306a36Sopenharmony_ci return 1; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci switch (irq_type) { 55562306a36Sopenharmony_ci case MAX77836_IRQ_INT1_ADC1K: 55662306a36Sopenharmony_ci info->irq_adc = true; 55762306a36Sopenharmony_ci return 1; 55862306a36Sopenharmony_ci case MAX77836_IRQ_INT2_VIDRM: 55962306a36Sopenharmony_ci /* Handle charger accessory */ 56062306a36Sopenharmony_ci info->irq_chg = true; 56162306a36Sopenharmony_ci return 1; 56262306a36Sopenharmony_ci default: 56362306a36Sopenharmony_ci return 0; 56462306a36Sopenharmony_ci } 56562306a36Sopenharmony_ci} 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_cistatic irqreturn_t max14577_muic_irq_handler(int irq, void *data) 56862306a36Sopenharmony_ci{ 56962306a36Sopenharmony_ci struct max14577_muic_info *info = data; 57062306a36Sopenharmony_ci int i, irq_type = -1; 57162306a36Sopenharmony_ci bool irq_parsed; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci /* 57462306a36Sopenharmony_ci * We may be called multiple times for different nested IRQ-s. 57562306a36Sopenharmony_ci * Including changes in INT1_ADC and INT2_CGHTYP at once. 57662306a36Sopenharmony_ci * However we only need to know whether it was ADC, charger 57762306a36Sopenharmony_ci * or both interrupts so decode IRQ and turn on proper flags. 57862306a36Sopenharmony_ci */ 57962306a36Sopenharmony_ci for (i = 0; i < info->muic_irqs_num; i++) 58062306a36Sopenharmony_ci if (irq == info->muic_irqs[i].virq) 58162306a36Sopenharmony_ci irq_type = info->muic_irqs[i].irq; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci switch (info->max14577->dev_type) { 58462306a36Sopenharmony_ci case MAXIM_DEVICE_TYPE_MAX77836: 58562306a36Sopenharmony_ci irq_parsed = max77836_parse_irq(info, irq_type); 58662306a36Sopenharmony_ci break; 58762306a36Sopenharmony_ci case MAXIM_DEVICE_TYPE_MAX14577: 58862306a36Sopenharmony_ci default: 58962306a36Sopenharmony_ci irq_parsed = max14577_parse_irq(info, irq_type); 59062306a36Sopenharmony_ci break; 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci if (!irq_parsed) { 59462306a36Sopenharmony_ci dev_err(info->dev, "muic interrupt: irq %d occurred, skipped\n", 59562306a36Sopenharmony_ci irq_type); 59662306a36Sopenharmony_ci return IRQ_HANDLED; 59762306a36Sopenharmony_ci } 59862306a36Sopenharmony_ci schedule_work(&info->irq_work); 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci return IRQ_HANDLED; 60162306a36Sopenharmony_ci} 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_cistatic int max14577_muic_detect_accessory(struct max14577_muic_info *info) 60462306a36Sopenharmony_ci{ 60562306a36Sopenharmony_ci int ret = 0; 60662306a36Sopenharmony_ci int adc; 60762306a36Sopenharmony_ci int chg_type; 60862306a36Sopenharmony_ci bool attached; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci mutex_lock(&info->mutex); 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci /* Read STATUSx register to detect accessory */ 61362306a36Sopenharmony_ci ret = max14577_bulk_read(info->max14577->regmap, 61462306a36Sopenharmony_ci MAX14577_MUIC_REG_STATUS1, info->status, 2); 61562306a36Sopenharmony_ci if (ret) { 61662306a36Sopenharmony_ci dev_err(info->dev, "failed to read MUIC register\n"); 61762306a36Sopenharmony_ci mutex_unlock(&info->mutex); 61862306a36Sopenharmony_ci return ret; 61962306a36Sopenharmony_ci } 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci adc = max14577_muic_get_cable_type(info, MAX14577_CABLE_GROUP_ADC, 62262306a36Sopenharmony_ci &attached); 62362306a36Sopenharmony_ci if (attached && adc != MAX14577_MUIC_ADC_OPEN) { 62462306a36Sopenharmony_ci ret = max14577_muic_adc_handler(info); 62562306a36Sopenharmony_ci if (ret < 0) { 62662306a36Sopenharmony_ci dev_err(info->dev, "Cannot detect accessory\n"); 62762306a36Sopenharmony_ci mutex_unlock(&info->mutex); 62862306a36Sopenharmony_ci return ret; 62962306a36Sopenharmony_ci } 63062306a36Sopenharmony_ci } 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci chg_type = max14577_muic_get_cable_type(info, MAX14577_CABLE_GROUP_CHG, 63362306a36Sopenharmony_ci &attached); 63462306a36Sopenharmony_ci if (attached && chg_type != MAX14577_CHARGER_TYPE_NONE) { 63562306a36Sopenharmony_ci ret = max14577_muic_chg_handler(info); 63662306a36Sopenharmony_ci if (ret < 0) { 63762306a36Sopenharmony_ci dev_err(info->dev, "Cannot detect charger accessory\n"); 63862306a36Sopenharmony_ci mutex_unlock(&info->mutex); 63962306a36Sopenharmony_ci return ret; 64062306a36Sopenharmony_ci } 64162306a36Sopenharmony_ci } 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci mutex_unlock(&info->mutex); 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci return 0; 64662306a36Sopenharmony_ci} 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_cistatic void max14577_muic_detect_cable_wq(struct work_struct *work) 64962306a36Sopenharmony_ci{ 65062306a36Sopenharmony_ci struct max14577_muic_info *info = container_of(to_delayed_work(work), 65162306a36Sopenharmony_ci struct max14577_muic_info, wq_detcable); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci max14577_muic_detect_accessory(info); 65462306a36Sopenharmony_ci} 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_cistatic int max14577_muic_probe(struct platform_device *pdev) 65762306a36Sopenharmony_ci{ 65862306a36Sopenharmony_ci struct max14577 *max14577 = dev_get_drvdata(pdev->dev.parent); 65962306a36Sopenharmony_ci struct max14577_muic_info *info; 66062306a36Sopenharmony_ci int delay_jiffies; 66162306a36Sopenharmony_ci int cable_type; 66262306a36Sopenharmony_ci bool attached; 66362306a36Sopenharmony_ci int ret; 66462306a36Sopenharmony_ci int i; 66562306a36Sopenharmony_ci u8 id; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); 66862306a36Sopenharmony_ci if (!info) 66962306a36Sopenharmony_ci return -ENOMEM; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci info->dev = &pdev->dev; 67262306a36Sopenharmony_ci info->max14577 = max14577; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci platform_set_drvdata(pdev, info); 67562306a36Sopenharmony_ci mutex_init(&info->mutex); 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci ret = devm_work_autocancel(&pdev->dev, &info->irq_work, 67862306a36Sopenharmony_ci max14577_muic_irq_work); 67962306a36Sopenharmony_ci if (ret) 68062306a36Sopenharmony_ci return ret; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci switch (max14577->dev_type) { 68362306a36Sopenharmony_ci case MAXIM_DEVICE_TYPE_MAX77836: 68462306a36Sopenharmony_ci info->muic_irqs = max77836_muic_irqs; 68562306a36Sopenharmony_ci info->muic_irqs_num = ARRAY_SIZE(max77836_muic_irqs); 68662306a36Sopenharmony_ci break; 68762306a36Sopenharmony_ci case MAXIM_DEVICE_TYPE_MAX14577: 68862306a36Sopenharmony_ci default: 68962306a36Sopenharmony_ci info->muic_irqs = max14577_muic_irqs; 69062306a36Sopenharmony_ci info->muic_irqs_num = ARRAY_SIZE(max14577_muic_irqs); 69162306a36Sopenharmony_ci } 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci /* Support irq domain for max14577 MUIC device */ 69462306a36Sopenharmony_ci for (i = 0; i < info->muic_irqs_num; i++) { 69562306a36Sopenharmony_ci struct max14577_muic_irq *muic_irq = &info->muic_irqs[i]; 69662306a36Sopenharmony_ci int virq = 0; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci virq = regmap_irq_get_virq(max14577->irq_data, muic_irq->irq); 69962306a36Sopenharmony_ci if (virq <= 0) 70062306a36Sopenharmony_ci return -EINVAL; 70162306a36Sopenharmony_ci muic_irq->virq = virq; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci ret = devm_request_threaded_irq(&pdev->dev, virq, NULL, 70462306a36Sopenharmony_ci max14577_muic_irq_handler, 70562306a36Sopenharmony_ci IRQF_NO_SUSPEND, 70662306a36Sopenharmony_ci muic_irq->name, info); 70762306a36Sopenharmony_ci if (ret) { 70862306a36Sopenharmony_ci dev_err(&pdev->dev, 70962306a36Sopenharmony_ci "failed: irq request (IRQ: %d, error :%d)\n", 71062306a36Sopenharmony_ci muic_irq->irq, ret); 71162306a36Sopenharmony_ci return ret; 71262306a36Sopenharmony_ci } 71362306a36Sopenharmony_ci } 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci /* Initialize extcon device */ 71662306a36Sopenharmony_ci info->edev = devm_extcon_dev_allocate(&pdev->dev, 71762306a36Sopenharmony_ci max14577_extcon_cable); 71862306a36Sopenharmony_ci if (IS_ERR(info->edev)) { 71962306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to allocate memory for extcon\n"); 72062306a36Sopenharmony_ci return PTR_ERR(info->edev); 72162306a36Sopenharmony_ci } 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci ret = devm_extcon_dev_register(&pdev->dev, info->edev); 72462306a36Sopenharmony_ci if (ret) { 72562306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to register extcon device\n"); 72662306a36Sopenharmony_ci return ret; 72762306a36Sopenharmony_ci } 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci /* Default h/w line path */ 73062306a36Sopenharmony_ci info->path_usb = CTRL1_SW_USB; 73162306a36Sopenharmony_ci info->path_uart = CTRL1_SW_UART; 73262306a36Sopenharmony_ci delay_jiffies = msecs_to_jiffies(DELAY_MS_DEFAULT); 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci /* Set initial path for UART when JIG is connected to get serial logs */ 73562306a36Sopenharmony_ci ret = max14577_bulk_read(info->max14577->regmap, 73662306a36Sopenharmony_ci MAX14577_MUIC_REG_STATUS1, info->status, 2); 73762306a36Sopenharmony_ci if (ret) { 73862306a36Sopenharmony_ci dev_err(info->dev, "Cannot read STATUS registers\n"); 73962306a36Sopenharmony_ci return ret; 74062306a36Sopenharmony_ci } 74162306a36Sopenharmony_ci cable_type = max14577_muic_get_cable_type(info, MAX14577_CABLE_GROUP_ADC, 74262306a36Sopenharmony_ci &attached); 74362306a36Sopenharmony_ci if (attached && cable_type == MAX14577_MUIC_ADC_FACTORY_MODE_UART_OFF) 74462306a36Sopenharmony_ci max14577_muic_set_path(info, info->path_uart, true); 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci /* Check revision number of MUIC device*/ 74762306a36Sopenharmony_ci ret = max14577_read_reg(info->max14577->regmap, 74862306a36Sopenharmony_ci MAX14577_REG_DEVICEID, &id); 74962306a36Sopenharmony_ci if (ret < 0) { 75062306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to read revision number\n"); 75162306a36Sopenharmony_ci return ret; 75262306a36Sopenharmony_ci } 75362306a36Sopenharmony_ci dev_info(info->dev, "device ID : 0x%x\n", id); 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci /* Set ADC debounce time */ 75662306a36Sopenharmony_ci max14577_muic_set_debounce_time(info, ADC_DEBOUNCE_TIME_25MS); 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, max14577_muic_detect_cable_wq); 76762306a36Sopenharmony_ci queue_delayed_work(system_power_efficient_wq, &info->wq_detcable, 76862306a36Sopenharmony_ci delay_jiffies); 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci return ret; 77162306a36Sopenharmony_ci} 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_cistatic const struct platform_device_id max14577_muic_id[] = { 77462306a36Sopenharmony_ci { "max14577-muic", MAXIM_DEVICE_TYPE_MAX14577, }, 77562306a36Sopenharmony_ci { "max77836-muic", MAXIM_DEVICE_TYPE_MAX77836, }, 77662306a36Sopenharmony_ci { } 77762306a36Sopenharmony_ci}; 77862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(platform, max14577_muic_id); 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_cistatic const struct of_device_id of_max14577_muic_dt_match[] = { 78162306a36Sopenharmony_ci { .compatible = "maxim,max14577-muic", 78262306a36Sopenharmony_ci .data = (void *)MAXIM_DEVICE_TYPE_MAX14577, }, 78362306a36Sopenharmony_ci { .compatible = "maxim,max77836-muic", 78462306a36Sopenharmony_ci .data = (void *)MAXIM_DEVICE_TYPE_MAX77836, }, 78562306a36Sopenharmony_ci { }, 78662306a36Sopenharmony_ci}; 78762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, of_max14577_muic_dt_match); 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_cistatic struct platform_driver max14577_muic_driver = { 79062306a36Sopenharmony_ci .driver = { 79162306a36Sopenharmony_ci .name = "max14577-muic", 79262306a36Sopenharmony_ci .of_match_table = of_max14577_muic_dt_match, 79362306a36Sopenharmony_ci }, 79462306a36Sopenharmony_ci .probe = max14577_muic_probe, 79562306a36Sopenharmony_ci .id_table = max14577_muic_id, 79662306a36Sopenharmony_ci}; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_cimodule_platform_driver(max14577_muic_driver); 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ciMODULE_DESCRIPTION("Maxim 14577/77836 Extcon driver"); 80162306a36Sopenharmony_ciMODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>, Krzysztof Kozlowski <krzk@kernel.org>"); 80262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 80362306a36Sopenharmony_ciMODULE_ALIAS("platform:extcon-max14577"); 804