/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright 2021 Google, LLC * */ #include #include #include #include #include #include #include #include "max77759.h" #include "max77759_charger.h" /* ----------------------------------------------------------------------- */ static void gs101_ext_bst_mode(struct max77759_usecase_data *uc_data, int mode); static int max77759_chgr_reg_write(struct i2c_client *client, u8 reg, u8 value) { struct max77759_chgr_data *data; if (!client) return -ENODEV; data = i2c_get_clientdata(client); if (!data || !data->regmap) return -ENODEV; return regmap_write(data->regmap, reg, value); } static int max77759_chgr_reg_read(struct i2c_client *client, u8 reg, u8 *value) { struct max77759_chgr_data *data; int ret, ival; if (!client) return -ENODEV; data = i2c_get_clientdata(client); if (!data || !data->regmap) return -ENODEV; ret = regmap_read(data->regmap, reg, &ival); if (ret == 0) *value = 0xFF & ival; return ret; } static int max77759_chgr_reg_update(struct i2c_client *client, u8 reg, u8 mask, u8 value) { struct max77759_chgr_data *data; if (!client) return -ENODEV; data = i2c_get_clientdata(client); if (!data || !data->regmap) return -ENODEV; return regmap_write_bits(data->regmap, reg, mask, value); } static int max77759_chgr_mode_write(struct i2c_client *client, enum max77759_charger_modes mode) { struct max77759_chgr_data *data; if (!client) return -ENODEV; data = i2c_get_clientdata(client); if (!data || !data->regmap) return -ENODEV; return regmap_write_bits(data->regmap, MAX77759_CHG_CNFG_00, MAX77759_CHG_CNFG_00_MODE_MASK, mode); } /* 1 if changed, 0 if not changed, or < 0 on error */ static int max77759_chgr_prot(struct regmap *regmap, bool enable) { u8 value = enable ? 0 : MAX77759_CHG_CNFG_06_CHGPROT_MASK; u8 prot; int ret, ival; ret = regmap_read(regmap, MAX77759_CHG_CNFG_06, &ival); if (ret < 0) return -EIO; prot = 0xFF & ival; if ((prot & MAX77759_CHG_CNFG_06_CHGPROT_MASK) == value) return 0; ret = regmap_write_bits(regmap, MAX77759_CHG_CNFG_06, MAX77759_CHG_CNFG_06_CHGPROT_MASK, value); if (ret < 0) return -EIO; return 1; } static int max77759_chgr_insel_write(struct i2c_client *client, u8 mask, u8 value) { struct max77759_chgr_data *data; int ret, prot; if (!client) return -ENODEV; data = i2c_get_clientdata(client); if (!data || !data->regmap) return -ENODEV; prot = max77759_chgr_prot(data->regmap, false); if (prot < 0) return -EIO; /* changing [CHGIN|WCIN]_INSEL: works when protection is disabled */ ret = regmap_write_bits(data->regmap, MAX77759_CHG_CNFG_12, mask, value); if (ret < 0 || prot == 0) return ret; prot = max77759_chgr_prot(data->regmap, true); if (prot < 0) { pr_err("%s: cannot restore protection bits (%d)\n", __func__, prot); return prot; }; return ret; } /* ----------------------------------------------------------------------- */ /* control VENDOR_EXTBST_CTRL (from TCPCI module) */ static int gs101_ls_mode(struct max77759_usecase_data *uc_data, int mode) { int ret; pr_debug("%s: mode=%d ext_bst_ctl=%d lsw1_c=%d lsw1_o=%d\n", __func__, mode, uc_data->ext_bst_ctl, uc_data->lsw1_is_closed, uc_data->lsw1_is_open); if (uc_data->ext_bst_ctl < 0) return 0; /* VENDOR_EXTBST_CTRL control LSW1, the read will check the state */ gpio_set_value_cansleep(uc_data->ext_bst_ctl, mode); /* b/182953320 load switch is optional */ if (uc_data->lsw1_is_open < 0 || uc_data->lsw1_is_closed < 0) return 0; /* ret <= 0 if *_is* is not true and > 1 if true */ switch (mode) { case 0: /* the OVP open right away */ ret = gpio_get_value_cansleep(uc_data->lsw1_is_open); if (ret <= 0 && uc_data->ls1_en > 0) { const int max_count = 3; int loops; /* do it manually and re-read after 20ms */ for (loops = 0; loops < max_count; loops++) { gpio_set_value_cansleep(uc_data->ls1_en, 0); usleep_range(20 * USEC_PER_MSEC, 20 * USEC_PER_MSEC + 100); ret = gpio_get_value_cansleep(uc_data->lsw1_is_open); pr_debug("%s: open lsw1 attempt %d/%d ret=%d\n", __func__, loops, max_count, ret); if (ret > 0) break; } } break; case 1: /* it takes 11 ms to turn on the OVP */ usleep_range(11 * USEC_PER_MSEC, 11 * USEC_PER_MSEC + 100); ret = gpio_get_value_cansleep(uc_data->lsw1_is_closed); break; default: return -EINVAL; } return (ret <= 0) ? -EIO : 0; } /* OVP LS2 */ #define OVP_LS2_MODE_OFF 0 #define OVP_LS2_MODE_ON 1 static int gs101_ls2_mode(struct max77759_usecase_data *uc_data, int mode) { pr_debug("%s: ls2_en=%d mode=%d\n", __func__, uc_data->ls2_en, mode); if (uc_data->ls2_en >= 0) gpio_set_value_cansleep(uc_data->ls2_en, !!mode); return 0; } /* control external boost mode * can be done controlling ls1, ls2 */ #define EXT_MODE_OFF 0 #define EXT_MODE_OTG_5_0V 1 #define EXT_MODE_OTG_7_5V 2 /* * bst_on=GPIO5 on Max77759 on canopy and on all whitefins, * bst_sel=Granville */ static int gs101_ext_mode(struct max77759_usecase_data *uc_data, int mode) { int ret = 0; pr_debug("%s: mode=%d on=%d sel=%d\n", __func__, mode, uc_data->bst_on, uc_data->bst_sel); if (uc_data->bst_on < 0) return 0; switch (mode) { case EXT_MODE_OFF: gpio_set_value_cansleep(uc_data->bst_on, 0); break; case EXT_MODE_OTG_5_0V: if (uc_data->bst_sel > 0) gpio_set_value_cansleep(uc_data->bst_sel, 0); msleep(100); gpio_set_value_cansleep(uc_data->bst_on, 1); break; case EXT_MODE_OTG_7_5V: /* TODO: verify this */ if (uc_data->bst_sel > 0) gpio_set_value_cansleep(uc_data->bst_sel, 1); msleep(100); gpio_set_value_cansleep(uc_data->bst_on, 1); break; default: return -EINVAL; } return ret; } int gs101_wlc_en(struct max77759_usecase_data *uc_data, bool wlc_on) { int ret = 0; pr_debug("%s: cpout_en=%d wlc_en=%d wlc_vbus_en=%d wlc_on=%d\n", __func__, uc_data->cpout_en, uc_data->wlc_en, uc_data->wlc_vbus_en, wlc_on); if (uc_data->cpout_en >= 0) { gpio_set_value_cansleep(uc_data->cpout_en, wlc_on); } else if (!wlc_on) { /* * when * uc_data->cpout_en != -EPROBE_DEFER && uc_data->wlc_en * could use uc_data->wlc_en with: * gpio_set_value_cansleep(uc_data->wlc_en, !!wlc_on); * * BUT need to resolve the race on start since toggling * ->wlc_en might not be undone by using ->cpout_en */ } /* b/202526678 */ if (uc_data->wlc_vbus_en >= 0) gpio_set_value_cansleep(uc_data->wlc_vbus_en, wlc_on); return ret; } EXPORT_SYMBOL_GPL(gs101_wlc_en); /* RTX reverse wireless charging */ static int gs101_wlc_tx_enable(struct max77759_usecase_data *uc_data, bool enable) { int ret = 0; if (enable) { if (!uc_data->wlctx_bst_en_first) ret = gs101_ls2_mode(uc_data, OVP_LS2_MODE_ON); if (ret == 0) ret = gs101_ext_mode(uc_data, EXT_MODE_OTG_7_5V); if (ret == 0 && uc_data->wlctx_bst_en_first) ret = gs101_ls2_mode(uc_data, OVP_LS2_MODE_ON); if (ret < 0) return ret; msleep(100); /* p9412 will not be in RX when powered from EXT */ ret = gs101_wlc_en(uc_data, true); if (ret < 0) return ret; if (uc_data->cpout21_en >= 0) gpio_set_value_cansleep(uc_data->cpout21_en, 0); } else { /* p9412 is already off from insel */ ret = gs101_wlc_en(uc_data, false); if (ret < 0) return ret; /* NOTE: turn off WLC, no need to reset cpout */ if (!uc_data->wlctx_bst_en_first) ret = gs101_ext_mode(uc_data, EXT_MODE_OFF); if (ret == 0) ret = gs101_ls2_mode(uc_data, OVP_LS2_MODE_OFF); if (ret == 0 && uc_data->wlctx_bst_en_first) ret = gs101_ext_mode(uc_data, EXT_MODE_OFF); /* STBY will re-enable WLC */ } return ret; } /* change p9412 CPOUT and adjust WCIN_REG */ #define GS101_WLCRX_CPOUT_DFLT 0 #define GS101_WLCRX_CPOUT_5_2V 1 static int gs101_cpout_mode(struct max77759_usecase_data *uc_data, int mode) { int ret; /* do not change MW unless p9412 can be changed as well */ if (uc_data->cpout_ctl < 0) return 0; if (mode == GS101_WLCRX_CPOUT_5_2V) { /* p9412: set CPOUT==5.2 only if on BPP */ gpio_set_value_cansleep(uc_data->cpout_ctl, 1); /* NOTE: no DC_IN to MW when WCIN_REG==4_85 unless CPOUT==5.2 */ ret = max77759_chgr_reg_update(uc_data->client, MAX77759_CHG_CNFG_12, MAX77759_CHG_CNFG_12_WCIN_REG_MASK, MAX77759_CHG_CNFG_12_WCIN_REG_4_85); } else { /* p9412: reset CPOUT to default */ gpio_set_value_cansleep(uc_data->cpout_ctl, 0); ret = max77759_chgr_reg_update(uc_data->client, MAX77759_CHG_CNFG_12, MAX77759_CHG_CNFG_12_WCIN_REG_MASK, MAX77759_CHG_CNFG_12_WCIN_REG_4_5); } return ret; } static int gs101_otg_bypass_enable(struct max77759_usecase_data *uc_data, u8 vbyp) { int ret; ret = max77759_chgr_reg_write(uc_data->client, MAX77759_CHG_CNFG_11, vbyp); if (ret < 0) { pr_err("%s: cannot set vbypset (%d)\n", __func__, ret); return ret; } return max77759_chgr_reg_update(uc_data->client, MAX77759_CHG_CNFG_18, MAX77759_CHG_CNFG_18_OTG_V_PGM, MAX77759_CHG_CNFG_18_OTG_V_PGM); } static int gs101_otg_bypass_disable(struct max77759_usecase_data *uc_data) { return max77759_chgr_reg_update(uc_data->client, MAX77759_CHG_CNFG_18, MAX77759_CHG_CNFG_18_OTG_V_PGM, 0); } static int gs101_otg_update_ilim(struct max77759_usecase_data *uc_data, int enable) { u8 ilim; if (uc_data->otg_orig == uc_data->otg_ilim) return 0; if (enable) { int rc; rc = max77759_chgr_reg_read(uc_data->client, MAX77759_CHG_CNFG_05, &uc_data->otg_orig); if (rc < 0) { pr_err("%s: cannot read otg_ilim (%d), use default\n", __func__, rc); uc_data->otg_orig = MAX77759_CHG_CNFG_05_OTG_ILIM_1500MA; } else { uc_data->otg_orig &= MAX77759_CHG_CNFG_05_OTG_ILIM_MASK; } ilim = uc_data->otg_ilim; } else { ilim = uc_data->otg_orig; } return max77759_chgr_reg_update(uc_data->client, MAX77759_CHG_CNFG_05, MAX77759_CHG_CNFG_05_OTG_ILIM_MASK, ilim); } static int gs101_otg_frs(struct max77759_usecase_data *uc_data, int enable) { int ret; ret = max77759_chgr_mode_write(uc_data->client, MAX77759_CHGR_MODE_OTG_BOOST_ON); if (ret < 0) { pr_err("%s: cannot set CNFG_00 to 0xa ret:%d\n", __func__, ret); return ret; } ret = gs101_otg_update_ilim(uc_data, enable); if (ret < 0) pr_err("%s: cannot update otg_ilim: %d\n", __func__, ret); return ret; } static int gs101_pogo_vout_enable(struct max77759_usecase_data *uc_data, bool enable) { int ret; pr_debug("%s: pogo_vout_en (%d)\n", __func__, enable); ret = gs101_ext_mode(uc_data, enable ? EXT_MODE_OTG_5_0V : EXT_MODE_OFF); if (ret == 0 && uc_data->pogo_vout_en > 0) gpio_set_value_cansleep(uc_data->pogo_vout_en, enable); return ret; } /* * Transition to standby (if needed) at the beginning of the sequences * @return <0 on error, 0 on success. ->use_case becomes GSU_MODE_STANDBY * if the transition is necessary (and successful). */ int gs101_to_standby(struct max77759_usecase_data *uc_data, int use_case) { const int from_uc = uc_data->use_case; bool need_stby = false; bool from_otg = false; int ret; switch (from_uc) { case GSU_MODE_USB_CHG: need_stby = use_case != GSU_MODE_USB_CHG_WLC_TX && use_case != GSU_MODE_WLC_RX && use_case != GSU_MODE_DOCK && use_case != GSU_MODE_USB_DC && use_case != GSU_MODE_USB_OTG_FRS && use_case != GSU_MODE_USB_CHG_POGO_VOUT; break; case GSU_MODE_WLC_RX: need_stby = use_case != GSU_MODE_USB_OTG_WLC_RX && use_case != GSU_MODE_WLC_DC; break; case GSU_MODE_WLC_TX: need_stby = use_case != GSU_MODE_USB_OTG_WLC_TX && use_case != GSU_MODE_USB_CHG_WLC_TX && use_case != GSU_MODE_USB_DC_WLC_TX && use_case != GSU_MODE_USB_OTG_FRS; break; case GSU_MODE_USB_CHG_WLC_TX: need_stby = use_case != GSU_MODE_USB_CHG && use_case != GSU_MODE_USB_OTG_WLC_TX && use_case != GSU_MODE_USB_DC; break; case GSU_MODE_USB_OTG: from_otg = true; if (use_case == GSU_MODE_USB_OTG_FRS) break; if (use_case == GSU_MODE_USB_OTG_WLC_TX) break; if (use_case == GSU_MODE_USB_OTG_WLC_RX) break; if (use_case == GSU_MODE_USB_OTG_WLC_DC) break; if (use_case == GSU_MODE_USB_OTG_POGO_VOUT) break; /* From 5. USB OTG to anything else, go to stby */ gs101_ext_bst_mode(uc_data, 0); ret = gs101_ls_mode(uc_data, 0); if (ret == 0) ret = gs101_ext_mode(uc_data, 0); if (ret < 0) return -EIO; /* TODO:Discharge IN/OUT with AO37 is done in TCPM */ msleep(100); need_stby = true; break; case GSU_MODE_USB_OTG_WLC_RX: from_otg = true; need_stby = use_case != GSU_MODE_WLC_RX && use_case != GSU_MODE_DOCK && use_case != GSU_MODE_USB_OTG; break; case GSU_MODE_USB_DC: need_stby = use_case != GSU_MODE_USB_DC; break; case GSU_MODE_WLC_DC: need_stby = use_case != GSU_MODE_WLC_DC; break; case GSU_MODE_USB_OTG_FRS: from_otg = true; need_stby = use_case != GSU_MODE_USB_OTG_FRS && use_case != GSU_MODE_USB_OTG_WLC_TX && use_case != GSU_MODE_USB_OTG_POGO_VOUT; break; /* * if (use_case == GSU_MODE_USB_OTG) * break; */ case GSU_RAW_MODE: need_stby = true; break; case GSU_MODE_USB_OTG_WLC_TX: case GSU_MODE_USB_OTG_POGO_VOUT: from_otg = true; need_stby = false; break; case GSU_MODE_POGO_VOUT: need_stby = use_case != GSU_MODE_USB_OTG_POGO_VOUT && use_case != GSU_MODE_USB_CHG_POGO_VOUT && use_case != GSU_MODE_USB_OTG_FRS; break; case GSU_MODE_USB_CHG_POGO_VOUT: need_stby = use_case != GSU_MODE_USB_CHG; break; case GSU_MODE_STANDBY: default: need_stby = false; break; } if (use_case == GSU_MODE_USB_WLC_RX || use_case == GSU_RAW_MODE) need_stby = true; pr_info("%s: use_case=%d->%d from_otg=%d need_stby=%d\n", __func__, from_uc, use_case, from_otg, need_stby); if (!need_stby) return 0; /* there are no ways out of OTG FRS */ if (from_uc == GSU_MODE_USB_OTG_FRS) { ret = gs101_otg_frs(uc_data, true); if (ret < 0) { pr_err("%s: cannot turn off OTG_FRS (%d)\n", __func__, ret); return ret; } } /* from WLC_TX to STBY */ if (from_uc == GSU_MODE_WLC_TX) { ret = gs101_wlc_tx_enable(uc_data, false); if (ret < 0) { pr_err("%s: cannot tun off wlc_tx (%d)\n", __func__, ret); return ret; } /* re-enable wlc IC if disabled */ ret = gs101_wlc_en(uc_data, true); if (ret < 0) pr_err("%s: cannot enable WLC (%d)\n", __func__, ret); } /* from WLC-DC to STBY */ if (from_uc == GSU_MODE_WLC_DC) { if (uc_data->dc_sw_gpio > 0) gpio_set_value_cansleep(uc_data->dc_sw_gpio, 0); ret = gs101_ext_mode(uc_data, EXT_MODE_OFF); if (ret < 0) { pr_debug("%s: cannot change extmode ret:%d\n", __func__, ret); return ret; } } /* * There is no direct transition to STBY from BPP_RX+OTG but we might * get here on error and when forcing raw values. This makes sure that * CPOUT is set to default. */ if (from_uc == GSU_MODE_USB_OTG_WLC_RX) { ret = gs101_cpout_mode(uc_data, GS101_WLCRX_CPOUT_DFLT); if (ret < 0) pr_err("%s: cannot reset cpout (%d)\n", __func__, ret); /* Reset the extbst mode */ if (uc_data->wlc_otg_extbst_en) gs101_ext_bst_mode(uc_data, 0); } /* from POGO_VOUT to STBY */ if (from_uc == GSU_MODE_POGO_VOUT) { ret = gs101_pogo_vout_enable(uc_data, false); if (ret < 0) pr_err("%s: cannot tun off pogo_vout (%d)\n", __func__, ret); } /* b/178458456 exit from all OTG cases need to reset the limit */ if (uc_data->otg_enable > 0) gpio_set_value_cansleep(uc_data->otg_enable, 0); /* transition to STBY (might need to be up) */ ret = max77759_chgr_mode_write(uc_data->client, MAX77759_CHGR_MODE_ALL_OFF); if (ret < 0) return -EIO; uc_data->use_case = GSU_MODE_STANDBY; return ret; } EXPORT_SYMBOL_GPL(gs101_to_standby); /* enable/disable soft-start. No need soft start from OTG->OTG_FRS */ static int gs101_ramp_bypass(struct max77759_usecase_data *uc_data, bool enable) { const u8 value = enable ? MAX77759_CHG_CNFG_00_BYPV_RAMP_BYPASS : 0; if (uc_data->is_a1 <= 0) return 0; return max77759_chgr_reg_update(uc_data->client, MAX77759_CHG_CNFG_00, MAX77759_CHG_CNFG_00_BYPV_RAMP_BYPASS, value); } /* cleanup from every usecase */ int gs101_force_standby(struct max77759_usecase_data *uc_data) { const u8 insel_mask = MAX77759_CHG_CNFG_12_CHGINSEL_MASK | MAX77759_CHG_CNFG_12_WCINSEL_MASK; const u8 insel_value = MAX77759_CHG_CNFG_12_CHGINSEL | MAX77759_CHG_CNFG_12_WCINSEL; int ret; pr_debug("%s: recovery\n", __func__); ret = gs101_ls_mode(uc_data, 0); if (ret < 0) pr_err("%s: cannot change ls_mode (%d)\n", __func__, ret); ret = gs101_ext_mode(uc_data, 0); if (ret < 0) pr_err("%s: cannot change ext mode (%d)\n", __func__, ret); ret = gs101_ramp_bypass(uc_data, false); if (ret < 0) pr_err("%s: cannot reset ramp_bypass (%d)\n", __func__, ret); ret = gs101_pogo_vout_enable(uc_data, false); if (ret < 0) pr_err("%s: cannot tun off pogo_vout (%d)\n", __func__, ret); ret = max77759_chgr_mode_write(uc_data->client, MAX77759_CHGR_MODE_ALL_OFF); if (ret < 0) pr_err("%s: cannot reset mode register (%d)\n", __func__, ret); ret = max77759_chgr_insel_write(uc_data->client, insel_mask, insel_value); if (ret < 0) pr_err("%s: cannot reset insel (%d)\n", __func__, ret); gs101_ext_bst_mode(uc_data, 0); return 0; } EXPORT_SYMBOL_GPL(gs101_force_standby); /* b/188488966 */ static int gs101_frs_to_otg(struct max77759_usecase_data *uc_data) { int closed, ret; ret = gs101_ext_mode(uc_data, EXT_MODE_OTG_5_0V); if (ret < 0) goto exit_done; msleep(100); if (uc_data->ls1_en > 0) gpio_set_value_cansleep(uc_data->ls1_en, 1); msleep(100); if (uc_data->lsw1_is_closed >= 0) closed = gpio_get_value_cansleep(uc_data->lsw1_is_closed); exit_done: pr_debug("%s: ls1_en=%d lsw1_is_closed=%d closed=%d ret=%d\n", __func__, uc_data->ls1_en, uc_data->lsw1_is_closed, closed, ret); return ret; } /* From OTG <-> OTG_FRS */ static int gs101_otg_mode(struct max77759_usecase_data *uc_data, int to) { int ret = -EINVAL; pr_debug("%s: to=%d\n", __func__, to); /* no transition needed if only use external boost OTG */ if (uc_data->ext_otg_only > 0) return 0; if (to == GSU_MODE_USB_OTG) { ret = gs101_ext_mode(uc_data, EXT_MODE_OTG_5_0V); if (ret < 0) return ret; usleep_range(5 * USEC_PER_MSEC, 5 * USEC_PER_MSEC + 100); ret = max77759_chgr_mode_write(uc_data->client, MAX77759_CHGR_MODE_ALL_OFF); } else if (to == GSU_MODE_USB_OTG_FRS) { int rc; ret = gs101_ramp_bypass(uc_data, true); if (ret == 0) ret = max77759_chgr_mode_write(uc_data->client, MAX77759_CHGR_MODE_OTG_BOOST_ON); if (ret < 0) return ret; usleep_range(5 * USEC_PER_MSEC, 5 * USEC_PER_MSEC + 100); rc = gs101_ramp_bypass(uc_data, false); if (rc < 0) pr_err("%s: cannot clear bypass rc:%d\n", __func__, rc); /* b/192986752 make sure that LSW1 is open before going to FRS */ rc = gs101_ls_mode(uc_data, 0); if (rc < 0) pr_err("%s: cannot clear lsw1 rc:%d\n", __func__, rc); ret = gs101_ext_mode(uc_data, EXT_MODE_OFF); if (ret < 0) return ret; } return ret; } /* * This must follow different paths depending on the platforms. * * When vin_is_valid is implemented the code uses the NBC workaround for MW * and the OVP. The code defaults to just setting the MODE register (at the * end of the use case) and toggling bst_on/bst_sel and setting ext_bst_ctl * otherwise. * * NOTE: the USB stack expects VBUS to be on after voting for the usecase. */ static int gs101_otg_enable(struct max77759_usecase_data *uc_data, int mode) { int ret, retn; /* the code default to write to the MODE register */ if (uc_data->vin_is_valid >= 0) { /* b/178458456 */ if (uc_data->otg_enable > 0) gpio_set_value_cansleep(uc_data->otg_enable, 1); /* NBC workaround */ ret = gs101_ls_mode(uc_data, 1); if (ret < 0) { pr_debug("%s: cannot close load switch (%d)\n", __func__, ret); return ret; } ret = gs101_otg_bypass_enable(uc_data, uc_data->otg_value); if (ret < 0) { pr_debug("%s: cannot set otg voltage (%d)\n", __func__, ret); return ret; } ret = max77759_chgr_mode_write(uc_data->client, MAX77759_CHGR_MODE_OTG_BOOST_ON); if (ret < 0) { pr_debug("%s: cannot set CNFG_00 to 0xa ret:%d\n", __func__, ret); return ret; } ret = gpio_get_value_cansleep(uc_data->vin_is_valid); if (ret == 0) { pr_debug("%s: VIN not VALID\n", __func__); ret = -EIO; goto reset_otg_voltage; } ret = gs101_ext_mode(uc_data, mode); if (ret < 0) pr_debug("%s: cannot change extmode ret:%d\n", __func__, ret); } else { ret = gs101_otg_bypass_enable(uc_data, uc_data->otg_value); if (ret < 0) { pr_debug("%s: cannot set otg voltage (%d)\n", __func__, ret); return ret; } /* ext mode is defined when ext boost is avalaible */ ret = gs101_ext_mode(uc_data, mode); if (ret < 0) { pr_debug("%s: cannot change extmode ret:%d\n", __func__, ret); goto reset_otg_voltage; } usleep_range(5 * USEC_PER_MSEC, 5 * USEC_PER_MSEC + 100); /* load switch from MW */ ret = gs101_ls_mode(uc_data, 1); if (ret < 0) { pr_debug("%s: cannot close load switch (%d)\n", __func__, ret); goto reset_otg_voltage; } /* time for VBUS to be on (could check PWRSTAT in MW) */ msleep(30); /* b/178458456 */ if (uc_data->otg_enable > 0) gpio_set_value_cansleep(uc_data->otg_enable, 0); } goto exit; reset_otg_voltage: retn = gs101_otg_bypass_disable(uc_data); if (retn < 0) pr_debug("%s: cannot reset otg voltage (%d)\n", __func__, retn); exit: return ret; } /* configure ilim wlctx */ static int gs101_wlctx_otg_en(struct max77759_usecase_data *uc_data, bool enable) { int ret; if (enable) { /* this should be already set */ if (uc_data->sw_en >= 0) gpio_set_value_cansleep(uc_data->sw_en, 1); ret = gs101_otg_update_ilim(uc_data, true); if (ret < 0) pr_err("%s: cannot update otg_ilim (%d)\n", __func__, ret); ret = gs101_otg_bypass_enable(uc_data, uc_data->otg_vbyp); if (ret < 0) pr_err("%s: cannot set otg_vbyp (%d)\n", __func__, ret); } else { /* TODO: Discharge IN/OUT nodes with AO37 should be done in TCPM */ msleep(100); /* "bad things will happen (tm)" if you force off ->sw_en */ ret = gs101_otg_update_ilim(uc_data, false); if (ret < 0) pr_err("%s: cannot restore otg_ilim (%d)\n", __func__, ret); /* TODO: restore initial value on !MAX77759_CHG_CNFG_11 */ ret = gs101_otg_bypass_disable(uc_data); if (ret < 0) pr_err("%s: cannot reset otg_v_pgm (%d)\n", __func__, ret); ret = 0; } return ret; } /* * Control the extbst mode enable/disable by the platform: * otg_fccm_reset = 1, ignore the ext_bst_mode control */ static void gs101_ext_bst_mode(struct max77759_usecase_data *uc_data, int mode) { struct max77759_chgr_data *data; pr_debug("%s: ext_bst_mode=%d mode=%d\n", __func__, uc_data->ext_bst_mode, mode); if (uc_data->ext_bst_mode <= 0) return; if (!uc_data->client) goto write_bst_mode; data = i2c_get_clientdata(uc_data->client); if (data && data->otg_fccm_reset) { pr_info("Set FCCM on 77759's callback\n"); return; } write_bst_mode: gpio_set_value_cansleep(uc_data->ext_bst_mode, mode); } /* * Case USB_chg USB_otg WLC_chg WLC_TX PMIC_Charger Ext_B LSxx Name * ------------------------------------------------------------------------------------- * 4-1 0 1 10 0 IF-PMIC-WCIN 1 1/0 USB_OTG_WLC_RX * 4-2 0 1 01 0 DC WCIN 1 1/0 USB_OTG_WLC_DC * 5-1 0 1 0 0 0 1 1/0 USB_OTG * 5-2 0 1 0 0 OTG_5V 0 0/0 USB_OTG_FRS * 7-2 0 1 0 1 OTG_5V 2 0/1 USB_OTG_WLC_TX * ------------------------------------------------------------------------------------- * WLC_chg = 0 off, 1 = on, 2 = PPS * Ext_Boost = 0 off, 1 = OTG 5V, 2 = WTX 7.5 * * 5-1: mode=0x0 in MW, EXT_B=1, LS1=1, LS2=0, IDLE <-> OTG (ext) * 5-2: mode=0xa in MW, EXT_B=0, LS1=0, LS2=0, IDLE <-> OTG_FRS * 7-2: mode=0xa in MW, EXT_B=2, LS1=0, LS2=1 * * AO37 + GPIO5 MW (canopy 3, whitev2p2) * . AO_ls1 <&max20339_gpio 0 GPIO_ACTIVE_HIGH> - bit0 * . AO_ls2 <&max20339_gpio 1 GPIO_ACTIVE_HIGH> - bit4 * * ls1 can be controlled poking the AO37 OR using a MW_GPIO _> EXT_BST_EN * * max77759,bst_on = <&max777x9_gpio 4 GPIO_ACTIVE_HIGH> * max77759,bst-sel = <&gpp27 3 GPIO_ACTIVE_HIGH> * max77759,bst_on=0, max77759,bst_sel=x => OFF * max77759,bst_on=1, max77759,bst_sel=0 => 5V * max77759,bst_on=1, max77759,bst_sel=1 => 7.5V * * Ext_Boost = 0 off * MW_gpio5 : Ext_B = 0, MW_gpio5 -> LOW * AO_ls1/ls2 : 0/0 * * Ext_Boost = 1 = OTG 5V * MW_gpio5 : Ext_B = 1, MW_gpio5 -> HIGH * AO_ls1/ls2 : 1/0 * * Ext_Boost = 2 WTX 7.5 * MW_gpio5 : Ext_B = 2, MW_gpio5 -> HIGH * AO_ls1/ls2 : 0/1 * * NOTE: do not call with (cb_data->wlc_rx && cb_data->wlc_tx) */ static int gs101_standby_to_otg(struct max77759_usecase_data *uc_data, int use_case) { int ret; const int mode = (uc_data->ext_otg_only || use_case != GSU_MODE_USB_OTG_FRS) ? EXT_MODE_OTG_5_0V : EXT_MODE_OFF; ret = gs101_otg_enable(uc_data, mode); if (ret == 0 && uc_data->ext_otg_only && !uc_data->wlc_otg_extbst_en) gs101_ext_bst_mode(uc_data, 1); if (ret == 0) usleep_range(5 * USEC_PER_MSEC, 5 * USEC_PER_MSEC + 100); /* * Assumption: gs101_to_usecase() will write back cached values to * CHG_CNFG_00.Mode. At the moment, the cached value at * max77759_mode_callback is 0. If the cached value changes to something * other than 0, then, the code has to be revisited. */ return ret; } /* was b/179816224 WLC_RX -> WLC_RX + OTG (Transition #10) */ static int gs101_wlcrx_to_wlcrx_otg(struct max77759_usecase_data *uc_data) { pr_warn("%s: disabled\n", __func__); return 0; } static int gs101_to_otg_usecase(struct max77759_usecase_data *uc_data, int use_case) { const int from_uc = uc_data->use_case; int ret = 0; switch (from_uc) { /* 5-1: #3: stby to USB OTG, mode = 1 */ /* 5-2: #3: stby to USB OTG_FRS, mode = 0 */ case GSU_MODE_STANDBY: ret = gs101_standby_to_otg(uc_data, use_case); if (ret < 0) { pr_err("%s: cannot enable OTG ret:%d\n", __func__, ret); return ret; } break; /* b/186535439 : USB_CHG->USB_OTG_FRS*/ case GSU_MODE_USB_CHG: case GSU_MODE_USB_CHG_WLC_TX: /* need to go through stby out of this */ if (use_case != GSU_MODE_USB_OTG_FRS && use_case != GSU_MODE_USB_OTG_WLC_TX) return -EINVAL; ret = gs101_otg_frs(uc_data, true); break; case GSU_MODE_WLC_TX: /* b/179820595: WLC_TX -> WLC_TX + OTG */ if (use_case == GSU_MODE_USB_OTG_WLC_TX) { ret = max77759_chgr_mode_write(uc_data->client, MAX77759_CHGR_MODE_OTG_BOOST_ON); if (ret < 0) { pr_err("%s: cannot set CNFG_00 to 0xa ret:%d\n", __func__, ret); return ret; } } break; case GSU_MODE_WLC_RX: case GSU_MODE_DOCK: if (use_case == GSU_MODE_USB_OTG_WLC_RX) { /* OTG+WLC set extbst mode to high */ if (uc_data->wlc_otg_extbst_en) gs101_ext_bst_mode(uc_data, 1); if (uc_data->rx_otg_en) { ret = gs101_cpout_mode(uc_data, GS101_WLCRX_CPOUT_5_2V); if (ret == 0) ret = gs101_standby_to_otg(uc_data, use_case); } else { ret = gs101_wlcrx_to_wlcrx_otg(uc_data); } } break; case GSU_MODE_USB_OTG: /* b/179820595: OTG -> WLC_TX + OTG (see b/181371696) */ if (use_case == GSU_MODE_USB_OTG_WLC_TX) { ret = gs101_otg_mode(uc_data, GSU_MODE_USB_OTG_FRS); if (ret == 0) ret = gs101_wlc_tx_enable(uc_data, true); } /* b/179816224: OTG -> WLC_RX + OTG */ if (use_case == GSU_MODE_USB_OTG_WLC_RX) { /* OTG+WLC set extbst mode to high */ if (uc_data->wlc_otg_extbst_en) { gs101_ext_bst_mode(uc_data, 1); ret = gs101_cpout_mode(uc_data, GS101_WLCRX_CPOUT_5_2V); } else { ret = gs101_cpout_mode(uc_data, GS101_WLCRX_CPOUT_5_2V); if (!ret) gs101_ext_bst_mode(uc_data, 1); } } if (use_case == GSU_MODE_USB_OTG_POGO_VOUT) { ret = gs101_otg_mode(uc_data, GSU_MODE_USB_OTG_FRS); if (ret == 0) ret = gs101_pogo_vout_enable(uc_data, true); } break; case GSU_MODE_USB_OTG_WLC_TX: /* b/179820595: WLC_TX + OTG -> OTG */ if (use_case == GSU_MODE_USB_OTG) { ret = gs101_wlc_tx_enable(uc_data, false); if (ret == 0) ret = gs101_frs_to_otg(uc_data); } break; case GSU_MODE_USB_OTG_WLC_RX: /* b/179816224: WLC_RX + OTG -> OTG */ if (use_case == GSU_MODE_USB_OTG) { /* it's in STBY, no need to reset gs101_otg_mode() */ if (!uc_data->ext_otg_only) { gs101_ext_bst_mode(uc_data, 0); ret = gs101_cpout_mode(uc_data, GS101_WLCRX_CPOUT_DFLT); } else { /* OTG only, set extbst mode to low */ ret = gs101_cpout_mode(uc_data, GS101_WLCRX_CPOUT_DFLT); if (uc_data->wlc_otg_extbst_en) gs101_ext_bst_mode(uc_data, 0); } } break; /* TODO: */ case GSU_MODE_USB_OTG_FRS: { if (use_case == GSU_MODE_USB_OTG_WLC_TX) { ret = gs101_wlc_tx_enable(uc_data, true); break; } if (use_case == GSU_MODE_USB_OTG_POGO_VOUT) { ret = gs101_pogo_vout_enable(uc_data, true); break; } /* * OTG source handover: OTG_FRS -> OTG * from EXT_BST (Regular OTG) to IF-PMIC OTG (FRS OTG) */ if (use_case != GSU_MODE_USB_OTG) return -EINVAL; /* TODO: */ } break; case GSU_MODE_POGO_VOUT: if (use_case == GSU_MODE_USB_OTG_POGO_VOUT) { ret = max77759_chgr_mode_write(uc_data->client, MAX77759_CHGR_MODE_OTG_BOOST_ON); if (ret < 0) { pr_err("%s: cannot set CNFG_00 to 0xa ret:%d\n", __func__, ret); return ret; } } break; case GSU_MODE_USB_OTG_POGO_VOUT: if (use_case == GSU_MODE_USB_OTG) { ret = gs101_pogo_vout_enable(uc_data, false); if (ret == 0) ret = gs101_frs_to_otg(uc_data); } break; default: return -ENOTSUPP; } return ret; } /* handles the transition data->use_case ==> use_case */ int gs101_to_usecase(struct max77759_usecase_data *uc_data, int use_case) { const int from_uc = uc_data->use_case; int ret = 0; switch (use_case) { case GSU_MODE_USB_OTG: case GSU_MODE_USB_OTG_FRS: case GSU_MODE_USB_OTG_WLC_RX: case GSU_MODE_USB_OTG_WLC_DC: case GSU_MODE_USB_OTG_WLC_TX: case GSU_MODE_USB_OTG_POGO_VOUT: ret = gs101_to_otg_usecase(uc_data, use_case); break; case GSU_MODE_WLC_TX: case GSU_MODE_USB_CHG_WLC_TX: /* Coex Case #4, WLC_TX + OTG -> WLC_TX */ if (from_uc == GSU_MODE_USB_OTG_WLC_TX) { ret = max77759_chgr_mode_write(uc_data->client, MAX77759_CHGR_MODE_ALL_OFF); if (ret == 0) ret = gs101_wlctx_otg_en(uc_data, false); } else { ret = gs101_wlc_tx_enable(uc_data, true); } break; case GSU_MODE_WLC_RX: case GSU_MODE_DOCK: if (from_uc == GSU_MODE_USB_OTG_WLC_RX) { /* to_stby brought to stby */ gs101_ext_bst_mode(uc_data, 0); if (uc_data->ext_otg_only) { if (ret == 0) ret = gs101_ls_mode(uc_data, 0); if (ret == 0) ret = gs101_ext_mode(uc_data, 0); if (ret == 0) ret = gs101_cpout_mode(uc_data, GS101_WLCRX_CPOUT_DFLT); } else { if (ret == 0) ret = gs101_cpout_mode(uc_data, GS101_WLCRX_CPOUT_DFLT); if (ret == 0) ret = gs101_otg_mode(uc_data, GSU_MODE_USB_OTG); } } if (from_uc == GSU_MODE_WLC_DC) { ret = gs101_ext_mode(uc_data, EXT_MODE_OFF); if (ret < 0) { pr_debug("%s: cannot change extmode ret:%d\n", __func__, ret); return ret; } } break; case GSU_MODE_USB_CHG: case GSU_MODE_USB_DC: if (from_uc == GSU_MODE_WLC_TX || from_uc == GSU_MODE_USB_CHG_WLC_TX) ret = gs101_wlc_tx_enable(uc_data, false); if (from_uc == GSU_MODE_USB_CHG_POGO_VOUT) ret = gs101_pogo_vout_enable(uc_data, false); /* b/232723240: charge over USB-C * set to 0 for POGO_OVP_EN * set to 1 for POGO_OVP_EN_L */ if (uc_data->pogo_ovp_en > 0) gpio_set_value_cansleep(uc_data->pogo_ovp_en, uc_data->pogo_ovp_en_act_low); break; case GSU_MODE_USB_WLC_RX: case GSU_RAW_MODE: /* just write the value to the register (it's in stby) */ break; case GSU_MODE_WLC_DC: if (uc_data->dc_sw_gpio > 0) gpio_set_value_cansleep(uc_data->dc_sw_gpio, 1); ret = gs101_ext_mode(uc_data, EXT_MODE_OTG_5_0V); if (ret < 0) { pr_debug("%s: cannot change extmode ret:%d\n", __func__, ret); return ret; } break; case GSU_MODE_POGO_VOUT: case GSU_MODE_USB_CHG_POGO_VOUT: if (from_uc == GSU_MODE_USB_OTG_POGO_VOUT) { ret = max77759_chgr_mode_write(uc_data->client, MAX77759_CHGR_MODE_ALL_OFF); } else { ret = gs101_pogo_vout_enable(uc_data, true); } break; default: break; } return ret; } EXPORT_SYMBOL_GPL(gs101_to_usecase); static int max77759_otg_ilim_ma_to_code(u8 *code, int otg_ilim) { if (otg_ilim == 0) *code = 0; else if (otg_ilim >= 500 && otg_ilim <= 1500) *code = 1 + (otg_ilim - 500) / 100; else return -EINVAL; return 0; } int max77759_otg_vbyp_mv_to_code(u8 *code, int vbyp) { if (vbyp >= 12000) *code = 0x8c; else if (vbyp >= 5000) *code = (vbyp - 5000) / 50; else return -EINVAL; return 0; } EXPORT_SYMBOL_GPL(max77759_otg_vbyp_mv_to_code); #define GS101_OTG_ILIM_DEFAULT_MA 1500 #define GS101_OTG_VBYPASS_DEFAULT_MV 5100 /* lazy init on the switches */ static bool gs101_setup_usecases_done(struct max77759_usecase_data *uc_data) { return (uc_data->cpout_en != -EPROBE_DEFER) && (uc_data->cpout_ctl != -EPROBE_DEFER) && (uc_data->wlc_vbus_en != -EPROBE_DEFER) && (uc_data->ext_bst_ctl != -EPROBE_DEFER) && (uc_data->bst_sel != -EPROBE_DEFER); /* TODO: handle platform specific differences.. uc_data->ls2_en != -EPROBE_DEFER && uc_data->lsw1_is_closed != -EPROBE_DEFER && uc_data->lsw1_is_open != -EPROBE_DEFER && uc_data->vin_is_valid != -EPROBE_DEFER && uc_data->cpout_ctl != -EPROBE_DEFER && uc_data->cpout_en != -EPROBE_DEFER && uc_data->cpout21_en != -EPROBE_DEFER uc_data->bst_on != -EPROBE_DEFER && uc_data->bst_sel != -EPROBE_DEFER && uc_data->ext_bst_ctl != -EPROBE_DEFER; */ } static void gs101_setup_default_usecase(struct max77759_usecase_data *uc_data) { int ret; uc_data->is_a1 = -1; uc_data->bst_on = -EPROBE_DEFER; uc_data->bst_sel = -EPROBE_DEFER; uc_data->ext_bst_ctl = -EPROBE_DEFER; uc_data->pogo_ovp_en = -EPROBE_DEFER; uc_data->pogo_vout_en = -EPROBE_DEFER; uc_data->ls1_en = -EPROBE_DEFER; uc_data->ls2_en = -EPROBE_DEFER; uc_data->sw_en = -EPROBE_DEFER; uc_data->vin_is_valid = -EPROBE_DEFER; uc_data->lsw1_is_closed = -EPROBE_DEFER; uc_data->lsw1_is_open = -EPROBE_DEFER; uc_data->otg_enable = -EPROBE_DEFER; uc_data->wlc_en = -EPROBE_DEFER; uc_data->wlc_vbus_en = -EPROBE_DEFER; uc_data->cpout_en = -EPROBE_DEFER; uc_data->cpout_ctl = -EPROBE_DEFER; uc_data->cpout21_en = -EPROBE_DEFER; uc_data->ext_bst_mode = -EPROBE_DEFER; uc_data->dc_sw_gpio = -EPROBE_DEFER; uc_data->init_done = false; /* TODO: override in bootloader and remove */ ret = max77759_otg_ilim_ma_to_code(&uc_data->otg_ilim, GS101_OTG_ILIM_DEFAULT_MA); if (ret < 0) uc_data->otg_ilim = MAX77759_CHG_CNFG_05_OTG_ILIM_1500MA; ret = max77759_chgr_reg_read(uc_data->client, MAX77759_CHG_CNFG_05, &uc_data->otg_orig); if (ret == 0) { uc_data->otg_orig &= MAX77759_CHG_CNFG_05_OTG_ILIM_MASK; } else { uc_data->otg_orig = uc_data->otg_ilim; } ret = max77759_otg_vbyp_mv_to_code(&uc_data->otg_vbyp, GS101_OTG_VBYPASS_DEFAULT_MV); if (ret < 0) uc_data->otg_vbyp = MAX77759_CHG_CNFG_11_OTG_VBYP_5100MV; } bool gs101_setup_usecases(struct max77759_usecase_data *uc_data, struct device_node *node) { enum of_gpio_flags flags = 0; if (!node) { gs101_setup_default_usecase(uc_data); return false; } /* control external boost if present */ if (uc_data->bst_on == -EPROBE_DEFER) uc_data->bst_on = of_get_named_gpio(node, "max77759,bst-on", 0); if (uc_data->bst_sel == -EPROBE_DEFER) uc_data->bst_sel = of_get_named_gpio(node, "max77759,bst-sel", 0); if (uc_data->ext_bst_ctl == -EPROBE_DEFER) uc_data->ext_bst_ctl = of_get_named_gpio(node, "max77759,extbst-ctl", 0); /* for enabling charging over pogo */ if (uc_data->pogo_ovp_en == -EPROBE_DEFER) { uc_data->pogo_ovp_en = of_get_named_gpio_flags(node, "max77759,pogo-ovp-en", 0, &flags); if (uc_data->pogo_ovp_en >= 0) uc_data->pogo_ovp_en_act_low = (flags & OF_GPIO_ACTIVE_LOW) ? 1 : 0; } if (uc_data->pogo_vout_en == -EPROBE_DEFER) { uc_data->pogo_vout_en = of_get_named_gpio(node, "max77759,pogo-vout-sw-en", 0); if (uc_data->pogo_vout_en >= 0) gpio_set_value_cansleep(uc_data->pogo_vout_en, 0); } /* NBC workaround */ if (uc_data->vin_is_valid == -EPROBE_DEFER) uc_data->vin_is_valid = of_get_named_gpio(node, "max77759,vin-is_valid", 0); if (uc_data->lsw1_is_closed == -EPROBE_DEFER) uc_data->lsw1_is_closed = of_get_named_gpio(node, "max77759,lsw1-is_closed", 0); if (uc_data->lsw1_is_open == -EPROBE_DEFER) uc_data->lsw1_is_open = of_get_named_gpio(node, "max77759,lsw1-is_open", 0); /* all OTG cases, change INOVLO */ if (uc_data->otg_enable == -EPROBE_DEFER) uc_data->otg_enable = of_get_named_gpio(node, "max77759,otg-enable", 0); /* wlc_rx: disable when chgin, CPOUT is safe */ if (uc_data->wlc_en == -EPROBE_DEFER) uc_data->wlc_en = of_get_named_gpio(node, "max77759,wlc-en", 0); if (uc_data->wlc_vbus_en == -EPROBE_DEFER) uc_data->wlc_vbus_en = of_get_named_gpio(node, "max77759,wlc-vbus_en", 0); /* wlc_rx -> wlc_rx+otg disable cpout */ if (uc_data->cpout_en == -EPROBE_DEFER) uc_data->cpout_en = of_get_named_gpio(node, "max77759,cpout-en", 0); /* to 5.2V in p9412 */ if (uc_data->cpout_ctl == -EPROBE_DEFER) uc_data->cpout_ctl = of_get_named_gpio(node, "max77759,cpout-ctl", 0); /* ->wlc_tx disable 2:1 cpout */ if (uc_data->cpout21_en == -EPROBE_DEFER) uc_data->cpout21_en = of_get_named_gpio(node, "max77759,cpout_21-en", 0); if (uc_data->ls1_en == -EPROBE_DEFER) uc_data->ls1_en = of_get_named_gpio(node, "max77759,ls1-en", 0); if (uc_data->ls2_en == -EPROBE_DEFER) uc_data->ls2_en = of_get_named_gpio(node, "max77759,ls2-en", 0); /* OTG+RTXL: IN-OUT switch of AO37 (forced always) */ if (uc_data->sw_en == -EPROBE_DEFER) uc_data->sw_en = of_get_named_gpio(node, "max77759,sw-en", 0); /* OPTIONAL: only in P1.1+ (TPS61372) */ if (uc_data->ext_bst_mode == -EPROBE_DEFER) uc_data->ext_bst_mode = of_get_named_gpio(node, "max77759,extbst-mode", 0); /* OPTIONAL: support wlc_rx -> wlc_rx+otg */ uc_data->rx_otg_en = of_property_read_bool(node, "max77759,rx-to-rx-otg-en"); /* OPTIONAL: support external boost OTG only */ uc_data->ext_otg_only = of_property_read_bool(node, "max77759,ext-otg-only"); /* OPTIONAL: use bst_on first on/off sequence */ uc_data->wlctx_bst_en_first = of_property_read_bool(node, "max77759,bst-lsw-sequence"); /* OPTIONAL: only extbst-enable when wlc+otg */ uc_data->wlc_otg_extbst_en = of_property_read_bool(node, "max77759,wlc-otg-extbst-en"); if (uc_data->dc_sw_gpio == -EPROBE_DEFER) uc_data->dc_sw_gpio = of_get_named_gpio(node, "max77759,gpio_dc_switch", 0); return gs101_setup_usecases_done(uc_data); } EXPORT_SYMBOL_GPL(gs101_setup_usecases); void gs101_dump_usecasase_config(struct max77759_usecase_data *uc_data) { pr_info("bst_on:%d, bst_sel:%d, ext_bst_ctl:%d\n", uc_data->bst_on, uc_data->bst_sel, uc_data->ext_bst_ctl); pr_info("vin_valid:%d lsw1_o:%d lsw1_c:%d\n", uc_data->vin_is_valid, uc_data->lsw1_is_open, uc_data->lsw1_is_closed); pr_info("wlc_en:%d wlc_vbus_en:%d cpout_en:%d cpout_ctl:%d cpout21_en=%d\n", uc_data->wlc_en, uc_data->wlc_vbus_en, uc_data->cpout_en, uc_data->cpout_ctl, uc_data->cpout21_en); pr_info("ls2_en:%d sw_en:%d ext_bst_mode:%d dc_sw_en:%d\n", uc_data->ls2_en, uc_data->sw_en, uc_data->ext_bst_mode, uc_data->dc_sw_gpio); pr_info("rx_to_rx_otg:%d ext_otg_only:%d\n", uc_data->rx_otg_en, uc_data->ext_otg_only); } EXPORT_SYMBOL_GPL(gs101_dump_usecasase_config);