Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Extcon charger detection driver for Intel Cherrytrail Whiskey Cove PMIC
0004  * Copyright (C) 2017 Hans de Goede <hdegoede@redhat.com>
0005  *
0006  * Based on various non upstream patches to support the CHT Whiskey Cove PMIC:
0007  * Copyright (C) 2013-2015 Intel Corporation. All rights reserved.
0008  */
0009 
0010 #include <linux/extcon-provider.h>
0011 #include <linux/interrupt.h>
0012 #include <linux/kernel.h>
0013 #include <linux/mfd/intel_soc_pmic.h>
0014 #include <linux/module.h>
0015 #include <linux/mod_devicetable.h>
0016 #include <linux/platform_device.h>
0017 #include <linux/power_supply.h>
0018 #include <linux/property.h>
0019 #include <linux/regmap.h>
0020 #include <linux/regulator/consumer.h>
0021 #include <linux/slab.h>
0022 #include <linux/usb/role.h>
0023 
0024 #include "extcon-intel.h"
0025 
0026 #define CHT_WC_PHYCTRL          0x5e07
0027 
0028 #define CHT_WC_CHGRCTRL0        0x5e16
0029 #define CHT_WC_CHGRCTRL0_CHGRRESET  BIT(0)
0030 #define CHT_WC_CHGRCTRL0_EMRGCHREN  BIT(1)
0031 #define CHT_WC_CHGRCTRL0_EXTCHRDIS  BIT(2)
0032 #define CHT_WC_CHGRCTRL0_SWCONTROL  BIT(3)
0033 #define CHT_WC_CHGRCTRL0_TTLCK      BIT(4)
0034 #define CHT_WC_CHGRCTRL0_CCSM_OFF   BIT(5)
0035 #define CHT_WC_CHGRCTRL0_DBPOFF     BIT(6)
0036 #define CHT_WC_CHGRCTRL0_CHR_WDT_NOKICK BIT(7)
0037 
0038 #define CHT_WC_CHGRCTRL1            0x5e17
0039 #define CHT_WC_CHGRCTRL1_FUSB_INLMT_100     BIT(0)
0040 #define CHT_WC_CHGRCTRL1_FUSB_INLMT_150     BIT(1)
0041 #define CHT_WC_CHGRCTRL1_FUSB_INLMT_500     BIT(2)
0042 #define CHT_WC_CHGRCTRL1_FUSB_INLMT_900     BIT(3)
0043 #define CHT_WC_CHGRCTRL1_FUSB_INLMT_1500    BIT(4)
0044 #define CHT_WC_CHGRCTRL1_FTEMP_EVENT        BIT(5)
0045 #define CHT_WC_CHGRCTRL1_OTGMODE        BIT(6)
0046 #define CHT_WC_CHGRCTRL1_DBPEN          BIT(7)
0047 
0048 #define CHT_WC_USBSRC           0x5e29
0049 #define CHT_WC_USBSRC_STS_MASK      GENMASK(1, 0)
0050 #define CHT_WC_USBSRC_STS_SUCCESS   2
0051 #define CHT_WC_USBSRC_STS_FAIL      3
0052 #define CHT_WC_USBSRC_TYPE_SHIFT    2
0053 #define CHT_WC_USBSRC_TYPE_MASK     GENMASK(5, 2)
0054 #define CHT_WC_USBSRC_TYPE_NONE     0
0055 #define CHT_WC_USBSRC_TYPE_SDP      1
0056 #define CHT_WC_USBSRC_TYPE_DCP      2
0057 #define CHT_WC_USBSRC_TYPE_CDP      3
0058 #define CHT_WC_USBSRC_TYPE_ACA      4
0059 #define CHT_WC_USBSRC_TYPE_SE1      5
0060 #define CHT_WC_USBSRC_TYPE_MHL      6
0061 #define CHT_WC_USBSRC_TYPE_FLOATING 7
0062 #define CHT_WC_USBSRC_TYPE_OTHER    8
0063 #define CHT_WC_USBSRC_TYPE_DCP_EXTPHY   9
0064 
0065 #define CHT_WC_CHGDISCTRL       0x5e2f
0066 #define CHT_WC_CHGDISCTRL_OUT       BIT(0)
0067 /* 0 - open drain, 1 - regular push-pull output */
0068 #define CHT_WC_CHGDISCTRL_DRV       BIT(4)
0069 /* 0 - pin is controlled by SW, 1 - by HW */
0070 #define CHT_WC_CHGDISCTRL_FN        BIT(6)
0071 
0072 #define CHT_WC_PWRSRC_IRQ       0x6e03
0073 #define CHT_WC_PWRSRC_IRQ_MASK      0x6e0f
0074 #define CHT_WC_PWRSRC_STS       0x6e1e
0075 #define CHT_WC_PWRSRC_VBUS      BIT(0)
0076 #define CHT_WC_PWRSRC_DC        BIT(1)
0077 #define CHT_WC_PWRSRC_BATT      BIT(2)
0078 #define CHT_WC_PWRSRC_USBID_MASK    GENMASK(4, 3)
0079 #define CHT_WC_PWRSRC_USBID_SHIFT   3
0080 #define CHT_WC_PWRSRC_RID_ACA       0
0081 #define CHT_WC_PWRSRC_RID_GND       1
0082 #define CHT_WC_PWRSRC_RID_FLOAT     2
0083 
0084 #define CHT_WC_VBUS_GPIO_CTLO       0x6e2d
0085 #define CHT_WC_VBUS_GPIO_CTLO_OUTPUT    BIT(0)
0086 #define CHT_WC_VBUS_GPIO_CTLO_DRV_OD    BIT(4)
0087 #define CHT_WC_VBUS_GPIO_CTLO_DIR_OUT   BIT(5)
0088 
0089 enum cht_wc_mux_select {
0090     MUX_SEL_PMIC = 0,
0091     MUX_SEL_SOC,
0092 };
0093 
0094 static const unsigned int cht_wc_extcon_cables[] = {
0095     EXTCON_USB,
0096     EXTCON_USB_HOST,
0097     EXTCON_CHG_USB_SDP,
0098     EXTCON_CHG_USB_CDP,
0099     EXTCON_CHG_USB_DCP,
0100     EXTCON_CHG_USB_ACA,
0101     EXTCON_NONE,
0102 };
0103 
0104 struct cht_wc_extcon_data {
0105     struct device *dev;
0106     struct regmap *regmap;
0107     struct extcon_dev *edev;
0108     struct usb_role_switch *role_sw;
0109     struct regulator *vbus_boost;
0110     struct power_supply *psy;
0111     enum power_supply_usb_type usb_type;
0112     unsigned int previous_cable;
0113     bool usb_host;
0114     bool vbus_boost_enabled;
0115 };
0116 
0117 static int cht_wc_extcon_get_id(struct cht_wc_extcon_data *ext, int pwrsrc_sts)
0118 {
0119     switch ((pwrsrc_sts & CHT_WC_PWRSRC_USBID_MASK) >> CHT_WC_PWRSRC_USBID_SHIFT) {
0120     case CHT_WC_PWRSRC_RID_GND:
0121         return INTEL_USB_ID_GND;
0122     case CHT_WC_PWRSRC_RID_FLOAT:
0123         return INTEL_USB_ID_FLOAT;
0124     /*
0125      * According to the spec. we should read the USB-ID pin ADC value here
0126      * to determine the resistance of the used pull-down resister and then
0127      * return RID_A / RID_B / RID_C based on this. But all "Accessory
0128      * Charger Adapter"s (ACAs) which users can actually buy always use
0129      * a combination of a charging port with one or more USB-A ports, so
0130      * they should always use a resistor indicating RID_A. But the spec
0131      * is hard to read / badly-worded so some of them actually indicate
0132      * they are a RID_B ACA evnen though they clearly are a RID_A ACA.
0133      * To workaround this simply always return INTEL_USB_RID_A, which
0134      * matches all the ACAs which users can actually buy.
0135      */
0136     case CHT_WC_PWRSRC_RID_ACA:
0137         return INTEL_USB_RID_A;
0138     default:
0139         return INTEL_USB_ID_FLOAT;
0140     }
0141 }
0142 
0143 static int cht_wc_extcon_get_charger(struct cht_wc_extcon_data *ext,
0144                      bool ignore_errors)
0145 {
0146     int ret, usbsrc, status;
0147     unsigned long timeout;
0148 
0149     /* Charger detection can take upto 600ms, wait 800ms max. */
0150     timeout = jiffies + msecs_to_jiffies(800);
0151     do {
0152         ret = regmap_read(ext->regmap, CHT_WC_USBSRC, &usbsrc);
0153         if (ret) {
0154             dev_err(ext->dev, "Error reading usbsrc: %d\n", ret);
0155             return ret;
0156         }
0157 
0158         status = usbsrc & CHT_WC_USBSRC_STS_MASK;
0159         if (status == CHT_WC_USBSRC_STS_SUCCESS ||
0160             status == CHT_WC_USBSRC_STS_FAIL)
0161             break;
0162 
0163         msleep(50); /* Wait a bit before retrying */
0164     } while (time_before(jiffies, timeout));
0165 
0166     if (status != CHT_WC_USBSRC_STS_SUCCESS) {
0167         if (!ignore_errors) {
0168             if (status == CHT_WC_USBSRC_STS_FAIL)
0169                 dev_warn(ext->dev, "Could not detect charger type\n");
0170             else
0171                 dev_warn(ext->dev, "Timeout detecting charger type\n");
0172         }
0173 
0174         /* Safe fallback */
0175         usbsrc = CHT_WC_USBSRC_TYPE_SDP << CHT_WC_USBSRC_TYPE_SHIFT;
0176     }
0177 
0178     usbsrc = (usbsrc & CHT_WC_USBSRC_TYPE_MASK) >> CHT_WC_USBSRC_TYPE_SHIFT;
0179     switch (usbsrc) {
0180     default:
0181         dev_warn(ext->dev,
0182             "Unhandled charger type %d, defaulting to SDP\n",
0183              ret);
0184         ext->usb_type = POWER_SUPPLY_USB_TYPE_SDP;
0185         return EXTCON_CHG_USB_SDP;
0186     case CHT_WC_USBSRC_TYPE_SDP:
0187     case CHT_WC_USBSRC_TYPE_FLOATING:
0188     case CHT_WC_USBSRC_TYPE_OTHER:
0189         ext->usb_type = POWER_SUPPLY_USB_TYPE_SDP;
0190         return EXTCON_CHG_USB_SDP;
0191     case CHT_WC_USBSRC_TYPE_CDP:
0192         ext->usb_type = POWER_SUPPLY_USB_TYPE_CDP;
0193         return EXTCON_CHG_USB_CDP;
0194     case CHT_WC_USBSRC_TYPE_DCP:
0195     case CHT_WC_USBSRC_TYPE_DCP_EXTPHY:
0196     case CHT_WC_USBSRC_TYPE_MHL: /* MHL2+ delivers upto 2A, treat as DCP */
0197         ext->usb_type = POWER_SUPPLY_USB_TYPE_DCP;
0198         return EXTCON_CHG_USB_DCP;
0199     case CHT_WC_USBSRC_TYPE_ACA:
0200         ext->usb_type = POWER_SUPPLY_USB_TYPE_ACA;
0201         return EXTCON_CHG_USB_ACA;
0202     }
0203 }
0204 
0205 static void cht_wc_extcon_set_phymux(struct cht_wc_extcon_data *ext, u8 state)
0206 {
0207     int ret;
0208 
0209     ret = regmap_write(ext->regmap, CHT_WC_PHYCTRL, state);
0210     if (ret)
0211         dev_err(ext->dev, "Error writing phyctrl: %d\n", ret);
0212 }
0213 
0214 static void cht_wc_extcon_set_5v_boost(struct cht_wc_extcon_data *ext,
0215                        bool enable)
0216 {
0217     int ret, val;
0218 
0219     /*
0220      * The 5V boost converter is enabled through a gpio on the PMIC, since
0221      * there currently is no gpio driver we access the gpio reg directly.
0222      */
0223     val = CHT_WC_VBUS_GPIO_CTLO_DRV_OD | CHT_WC_VBUS_GPIO_CTLO_DIR_OUT;
0224     if (enable)
0225         val |= CHT_WC_VBUS_GPIO_CTLO_OUTPUT;
0226 
0227     ret = regmap_write(ext->regmap, CHT_WC_VBUS_GPIO_CTLO, val);
0228     if (ret)
0229         dev_err(ext->dev, "Error writing Vbus GPIO CTLO: %d\n", ret);
0230 }
0231 
0232 static void cht_wc_extcon_set_otgmode(struct cht_wc_extcon_data *ext,
0233                       bool enable)
0234 {
0235     unsigned int val = enable ? CHT_WC_CHGRCTRL1_OTGMODE : 0;
0236     int ret;
0237 
0238     ret = regmap_update_bits(ext->regmap, CHT_WC_CHGRCTRL1,
0239                  CHT_WC_CHGRCTRL1_OTGMODE, val);
0240     if (ret)
0241         dev_err(ext->dev, "Error updating CHGRCTRL1 reg: %d\n", ret);
0242 
0243     if (ext->vbus_boost && ext->vbus_boost_enabled != enable) {
0244         if (enable)
0245             ret = regulator_enable(ext->vbus_boost);
0246         else
0247             ret = regulator_disable(ext->vbus_boost);
0248 
0249         if (ret)
0250             dev_err(ext->dev, "Error updating Vbus boost regulator: %d\n", ret);
0251         else
0252             ext->vbus_boost_enabled = enable;
0253     }
0254 }
0255 
0256 static void cht_wc_extcon_enable_charging(struct cht_wc_extcon_data *ext,
0257                       bool enable)
0258 {
0259     unsigned int val = enable ? 0 : CHT_WC_CHGDISCTRL_OUT;
0260     int ret;
0261 
0262     ret = regmap_update_bits(ext->regmap, CHT_WC_CHGDISCTRL,
0263                  CHT_WC_CHGDISCTRL_OUT, val);
0264     if (ret)
0265         dev_err(ext->dev, "Error updating CHGDISCTRL reg: %d\n", ret);
0266 }
0267 
0268 /* Small helper to sync EXTCON_CHG_USB_SDP and EXTCON_USB state */
0269 static void cht_wc_extcon_set_state(struct cht_wc_extcon_data *ext,
0270                     unsigned int cable, bool state)
0271 {
0272     extcon_set_state_sync(ext->edev, cable, state);
0273     if (cable == EXTCON_CHG_USB_SDP)
0274         extcon_set_state_sync(ext->edev, EXTCON_USB, state);
0275 }
0276 
0277 static void cht_wc_extcon_pwrsrc_event(struct cht_wc_extcon_data *ext)
0278 {
0279     int ret, pwrsrc_sts, id;
0280     unsigned int cable = EXTCON_NONE;
0281     /* Ignore errors in host mode, as the 5v boost converter is on then */
0282     bool ignore_get_charger_errors = ext->usb_host;
0283     enum usb_role role;
0284 
0285     ext->usb_type = POWER_SUPPLY_USB_TYPE_UNKNOWN;
0286 
0287     ret = regmap_read(ext->regmap, CHT_WC_PWRSRC_STS, &pwrsrc_sts);
0288     if (ret) {
0289         dev_err(ext->dev, "Error reading pwrsrc status: %d\n", ret);
0290         return;
0291     }
0292 
0293     id = cht_wc_extcon_get_id(ext, pwrsrc_sts);
0294     if (id == INTEL_USB_ID_GND) {
0295         cht_wc_extcon_enable_charging(ext, false);
0296         cht_wc_extcon_set_otgmode(ext, true);
0297 
0298         /* The 5v boost causes a false VBUS / SDP detect, skip */
0299         goto charger_det_done;
0300     }
0301 
0302     cht_wc_extcon_set_otgmode(ext, false);
0303     cht_wc_extcon_enable_charging(ext, true);
0304 
0305     /* Plugged into a host/charger or not connected? */
0306     if (!(pwrsrc_sts & CHT_WC_PWRSRC_VBUS)) {
0307         /* Route D+ and D- to PMIC for future charger detection */
0308         cht_wc_extcon_set_phymux(ext, MUX_SEL_PMIC);
0309         goto set_state;
0310     }
0311 
0312     ret = cht_wc_extcon_get_charger(ext, ignore_get_charger_errors);
0313     if (ret >= 0)
0314         cable = ret;
0315 
0316 charger_det_done:
0317     /* Route D+ and D- to SoC for the host or gadget controller */
0318     cht_wc_extcon_set_phymux(ext, MUX_SEL_SOC);
0319 
0320 set_state:
0321     if (cable != ext->previous_cable) {
0322         cht_wc_extcon_set_state(ext, cable, true);
0323         cht_wc_extcon_set_state(ext, ext->previous_cable, false);
0324         ext->previous_cable = cable;
0325     }
0326 
0327     ext->usb_host = ((id == INTEL_USB_ID_GND) || (id == INTEL_USB_RID_A));
0328     extcon_set_state_sync(ext->edev, EXTCON_USB_HOST, ext->usb_host);
0329 
0330     if (ext->usb_host)
0331         role = USB_ROLE_HOST;
0332     else if (pwrsrc_sts & CHT_WC_PWRSRC_VBUS)
0333         role = USB_ROLE_DEVICE;
0334     else
0335         role = USB_ROLE_NONE;
0336 
0337     /* Note: this is a no-op when ext->role_sw is NULL */
0338     ret = usb_role_switch_set_role(ext->role_sw, role);
0339     if (ret)
0340         dev_err(ext->dev, "Error setting USB-role: %d\n", ret);
0341 
0342     if (ext->psy)
0343         power_supply_changed(ext->psy);
0344 }
0345 
0346 static irqreturn_t cht_wc_extcon_isr(int irq, void *data)
0347 {
0348     struct cht_wc_extcon_data *ext = data;
0349     int ret, irqs;
0350 
0351     ret = regmap_read(ext->regmap, CHT_WC_PWRSRC_IRQ, &irqs);
0352     if (ret) {
0353         dev_err(ext->dev, "Error reading irqs: %d\n", ret);
0354         return IRQ_NONE;
0355     }
0356 
0357     cht_wc_extcon_pwrsrc_event(ext);
0358 
0359     ret = regmap_write(ext->regmap, CHT_WC_PWRSRC_IRQ, irqs);
0360     if (ret) {
0361         dev_err(ext->dev, "Error writing irqs: %d\n", ret);
0362         return IRQ_NONE;
0363     }
0364 
0365     return IRQ_HANDLED;
0366 }
0367 
0368 static int cht_wc_extcon_sw_control(struct cht_wc_extcon_data *ext, bool enable)
0369 {
0370     int ret, mask, val;
0371 
0372     val = enable ? 0 : CHT_WC_CHGDISCTRL_FN;
0373     ret = regmap_update_bits(ext->regmap, CHT_WC_CHGDISCTRL,
0374                  CHT_WC_CHGDISCTRL_FN, val);
0375     if (ret)
0376         dev_err(ext->dev,
0377             "Error setting sw control for CHGDIS pin: %d\n",
0378             ret);
0379 
0380     mask = CHT_WC_CHGRCTRL0_SWCONTROL | CHT_WC_CHGRCTRL0_CCSM_OFF;
0381     val = enable ? mask : 0;
0382     ret = regmap_update_bits(ext->regmap, CHT_WC_CHGRCTRL0, mask, val);
0383     if (ret)
0384         dev_err(ext->dev, "Error setting sw control: %d\n", ret);
0385 
0386     return ret;
0387 }
0388 
0389 static int cht_wc_extcon_find_role_sw(struct cht_wc_extcon_data *ext)
0390 {
0391     const struct software_node *swnode;
0392     struct fwnode_handle *fwnode;
0393 
0394     swnode = software_node_find_by_name(NULL, "intel-xhci-usb-sw");
0395     if (!swnode)
0396         return -EPROBE_DEFER;
0397 
0398     fwnode = software_node_fwnode(swnode);
0399     ext->role_sw = usb_role_switch_find_by_fwnode(fwnode);
0400     fwnode_handle_put(fwnode);
0401 
0402     return ext->role_sw ? 0 : -EPROBE_DEFER;
0403 }
0404 
0405 static void cht_wc_extcon_put_role_sw(void *data)
0406 {
0407     struct cht_wc_extcon_data *ext = data;
0408 
0409     usb_role_switch_put(ext->role_sw);
0410 }
0411 
0412 /* Some boards require controlling the role-sw and Vbus based on the id-pin */
0413 static int cht_wc_extcon_get_role_sw_and_regulator(struct cht_wc_extcon_data *ext)
0414 {
0415     int ret;
0416 
0417     ret = cht_wc_extcon_find_role_sw(ext);
0418     if (ret)
0419         return ret;
0420 
0421     ret = devm_add_action_or_reset(ext->dev, cht_wc_extcon_put_role_sw, ext);
0422     if (ret)
0423         return ret;
0424 
0425     /*
0426      * On x86/ACPI platforms the regulator <-> consumer link is provided
0427      * by platform_data passed to the regulator driver. This means that
0428      * this info is not available before the regulator driver has bound.
0429      * Use devm_regulator_get_optional() to avoid getting a dummy
0430      * regulator and wait for the regulator to show up if necessary.
0431      */
0432     ext->vbus_boost = devm_regulator_get_optional(ext->dev, "vbus");
0433     if (IS_ERR(ext->vbus_boost)) {
0434         ret = PTR_ERR(ext->vbus_boost);
0435         if (ret == -ENODEV)
0436             ret = -EPROBE_DEFER;
0437 
0438         return dev_err_probe(ext->dev, ret, "getting Vbus regulator");
0439     }
0440 
0441     return 0;
0442 }
0443 
0444 static int cht_wc_extcon_psy_get_prop(struct power_supply *psy,
0445                       enum power_supply_property psp,
0446                       union power_supply_propval *val)
0447 {
0448     struct cht_wc_extcon_data *ext = power_supply_get_drvdata(psy);
0449 
0450     switch (psp) {
0451     case POWER_SUPPLY_PROP_USB_TYPE:
0452         val->intval = ext->usb_type;
0453         break;
0454     case POWER_SUPPLY_PROP_ONLINE:
0455         val->intval = ext->usb_type ? 1 : 0;
0456         break;
0457     default:
0458         return -EINVAL;
0459     }
0460 
0461     return 0;
0462 }
0463 
0464 static const enum power_supply_usb_type cht_wc_extcon_psy_usb_types[] = {
0465     POWER_SUPPLY_USB_TYPE_SDP,
0466     POWER_SUPPLY_USB_TYPE_CDP,
0467     POWER_SUPPLY_USB_TYPE_DCP,
0468     POWER_SUPPLY_USB_TYPE_ACA,
0469     POWER_SUPPLY_USB_TYPE_UNKNOWN,
0470 };
0471 
0472 static const enum power_supply_property cht_wc_extcon_psy_props[] = {
0473     POWER_SUPPLY_PROP_USB_TYPE,
0474     POWER_SUPPLY_PROP_ONLINE,
0475 };
0476 
0477 static const struct power_supply_desc cht_wc_extcon_psy_desc = {
0478     .name = "cht_wcove_pwrsrc",
0479     .type = POWER_SUPPLY_TYPE_USB,
0480     .usb_types = cht_wc_extcon_psy_usb_types,
0481     .num_usb_types = ARRAY_SIZE(cht_wc_extcon_psy_usb_types),
0482     .properties = cht_wc_extcon_psy_props,
0483     .num_properties = ARRAY_SIZE(cht_wc_extcon_psy_props),
0484     .get_property = cht_wc_extcon_psy_get_prop,
0485 };
0486 
0487 static int cht_wc_extcon_register_psy(struct cht_wc_extcon_data *ext)
0488 {
0489     struct power_supply_config psy_cfg = { .drv_data = ext };
0490 
0491     ext->psy = devm_power_supply_register(ext->dev,
0492                           &cht_wc_extcon_psy_desc,
0493                           &psy_cfg);
0494     return PTR_ERR_OR_ZERO(ext->psy);
0495 }
0496 
0497 static int cht_wc_extcon_probe(struct platform_device *pdev)
0498 {
0499     struct intel_soc_pmic *pmic = dev_get_drvdata(pdev->dev.parent);
0500     struct cht_wc_extcon_data *ext;
0501     unsigned long mask = ~(CHT_WC_PWRSRC_VBUS | CHT_WC_PWRSRC_USBID_MASK);
0502     int pwrsrc_sts, id;
0503     int irq, ret;
0504 
0505     irq = platform_get_irq(pdev, 0);
0506     if (irq < 0)
0507         return irq;
0508 
0509     ext = devm_kzalloc(&pdev->dev, sizeof(*ext), GFP_KERNEL);
0510     if (!ext)
0511         return -ENOMEM;
0512 
0513     ext->dev = &pdev->dev;
0514     ext->regmap = pmic->regmap;
0515     ext->previous_cable = EXTCON_NONE;
0516 
0517     /* Initialize extcon device */
0518     ext->edev = devm_extcon_dev_allocate(ext->dev, cht_wc_extcon_cables);
0519     if (IS_ERR(ext->edev))
0520         return PTR_ERR(ext->edev);
0521 
0522     switch (pmic->cht_wc_model) {
0523     case INTEL_CHT_WC_GPD_WIN_POCKET:
0524         /*
0525          * When a host-cable is detected the BIOS enables an external 5v boost
0526          * converter to power connected devices there are 2 problems with this:
0527          * 1) This gets seen by the external battery charger as a valid Vbus
0528          *    supply and it then tries to feed Vsys from this creating a
0529          *    feedback loop which causes aprox. 300 mA extra battery drain
0530          *    (and unless we drive the external-charger-disable pin high it
0531          *    also tries to charge the battery causing even more feedback).
0532          * 2) This gets seen by the pwrsrc block as a SDP USB Vbus supply
0533          * Since the external battery charger has its own 5v boost converter
0534          * which does not have these issues, we simply turn the separate
0535          * external 5v boost converter off and leave it off entirely.
0536          */
0537         cht_wc_extcon_set_5v_boost(ext, false);
0538         break;
0539     case INTEL_CHT_WC_LENOVO_YOGABOOK1:
0540         /* Do this first, as it may very well return -EPROBE_DEFER. */
0541         ret = cht_wc_extcon_get_role_sw_and_regulator(ext);
0542         if (ret)
0543             return ret;
0544         /*
0545          * The bq25890 used here relies on this driver's BC-1.2 charger
0546          * detection, and the bq25890 driver expect this info to be
0547          * available through a parent power_supply class device which
0548          * models the detected charger (idem to how the Type-C TCPM code
0549          * registers a power_supply classdev for the connected charger).
0550          */
0551         ret = cht_wc_extcon_register_psy(ext);
0552         if (ret)
0553             return ret;
0554         break;
0555     case INTEL_CHT_WC_XIAOMI_MIPAD2:
0556         ret = cht_wc_extcon_get_role_sw_and_regulator(ext);
0557         if (ret)
0558             return ret;
0559         break;
0560     default:
0561         break;
0562     }
0563 
0564     /* Enable sw control */
0565     ret = cht_wc_extcon_sw_control(ext, true);
0566     if (ret)
0567         goto disable_sw_control;
0568 
0569     /* Disable charging by external battery charger */
0570     cht_wc_extcon_enable_charging(ext, false);
0571 
0572     /* Register extcon device */
0573     ret = devm_extcon_dev_register(ext->dev, ext->edev);
0574     if (ret) {
0575         dev_err(ext->dev, "Error registering extcon device: %d\n", ret);
0576         goto disable_sw_control;
0577     }
0578 
0579     ret = regmap_read(ext->regmap, CHT_WC_PWRSRC_STS, &pwrsrc_sts);
0580     if (ret) {
0581         dev_err(ext->dev, "Error reading pwrsrc status: %d\n", ret);
0582         goto disable_sw_control;
0583     }
0584 
0585     /*
0586      * If no USB host or device connected, route D+ and D- to PMIC for
0587      * initial charger detection
0588      */
0589     id = cht_wc_extcon_get_id(ext, pwrsrc_sts);
0590     if (id != INTEL_USB_ID_GND)
0591         cht_wc_extcon_set_phymux(ext, MUX_SEL_PMIC);
0592 
0593     /* Get initial state */
0594     cht_wc_extcon_pwrsrc_event(ext);
0595 
0596     ret = devm_request_threaded_irq(ext->dev, irq, NULL, cht_wc_extcon_isr,
0597                     IRQF_ONESHOT, pdev->name, ext);
0598     if (ret) {
0599         dev_err(ext->dev, "Error requesting interrupt: %d\n", ret);
0600         goto disable_sw_control;
0601     }
0602 
0603     /* Unmask irqs */
0604     ret = regmap_write(ext->regmap, CHT_WC_PWRSRC_IRQ_MASK, mask);
0605     if (ret) {
0606         dev_err(ext->dev, "Error writing irq-mask: %d\n", ret);
0607         goto disable_sw_control;
0608     }
0609 
0610     platform_set_drvdata(pdev, ext);
0611 
0612     return 0;
0613 
0614 disable_sw_control:
0615     cht_wc_extcon_sw_control(ext, false);
0616     return ret;
0617 }
0618 
0619 static int cht_wc_extcon_remove(struct platform_device *pdev)
0620 {
0621     struct cht_wc_extcon_data *ext = platform_get_drvdata(pdev);
0622 
0623     cht_wc_extcon_sw_control(ext, false);
0624 
0625     return 0;
0626 }
0627 
0628 static const struct platform_device_id cht_wc_extcon_table[] = {
0629     { .name = "cht_wcove_pwrsrc" },
0630     {},
0631 };
0632 MODULE_DEVICE_TABLE(platform, cht_wc_extcon_table);
0633 
0634 static struct platform_driver cht_wc_extcon_driver = {
0635     .probe = cht_wc_extcon_probe,
0636     .remove = cht_wc_extcon_remove,
0637     .id_table = cht_wc_extcon_table,
0638     .driver = {
0639         .name = "cht_wcove_pwrsrc",
0640     },
0641 };
0642 module_platform_driver(cht_wc_extcon_driver);
0643 
0644 MODULE_DESCRIPTION("Intel Cherrytrail Whiskey Cove PMIC extcon driver");
0645 MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
0646 MODULE_LICENSE("GPL v2");