cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

extcon-intel-cht-wc.c (18806B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Extcon charger detection driver for Intel Cherrytrail Whiskey Cove PMIC
      4 * Copyright (C) 2017 Hans de Goede <hdegoede@redhat.com>
      5 *
      6 * Based on various non upstream patches to support the CHT Whiskey Cove PMIC:
      7 * Copyright (C) 2013-2015 Intel Corporation. All rights reserved.
      8 */
      9
     10#include <linux/extcon-provider.h>
     11#include <linux/interrupt.h>
     12#include <linux/kernel.h>
     13#include <linux/mfd/intel_soc_pmic.h>
     14#include <linux/module.h>
     15#include <linux/mod_devicetable.h>
     16#include <linux/platform_device.h>
     17#include <linux/power_supply.h>
     18#include <linux/property.h>
     19#include <linux/regmap.h>
     20#include <linux/regulator/consumer.h>
     21#include <linux/slab.h>
     22#include <linux/usb/role.h>
     23
     24#include "extcon-intel.h"
     25
     26#define CHT_WC_PHYCTRL			0x5e07
     27
     28#define CHT_WC_CHGRCTRL0		0x5e16
     29#define CHT_WC_CHGRCTRL0_CHGRRESET	BIT(0)
     30#define CHT_WC_CHGRCTRL0_EMRGCHREN	BIT(1)
     31#define CHT_WC_CHGRCTRL0_EXTCHRDIS	BIT(2)
     32#define CHT_WC_CHGRCTRL0_SWCONTROL	BIT(3)
     33#define CHT_WC_CHGRCTRL0_TTLCK		BIT(4)
     34#define CHT_WC_CHGRCTRL0_CCSM_OFF	BIT(5)
     35#define CHT_WC_CHGRCTRL0_DBPOFF		BIT(6)
     36#define CHT_WC_CHGRCTRL0_CHR_WDT_NOKICK	BIT(7)
     37
     38#define CHT_WC_CHGRCTRL1			0x5e17
     39#define CHT_WC_CHGRCTRL1_FUSB_INLMT_100		BIT(0)
     40#define CHT_WC_CHGRCTRL1_FUSB_INLMT_150		BIT(1)
     41#define CHT_WC_CHGRCTRL1_FUSB_INLMT_500		BIT(2)
     42#define CHT_WC_CHGRCTRL1_FUSB_INLMT_900		BIT(3)
     43#define CHT_WC_CHGRCTRL1_FUSB_INLMT_1500	BIT(4)
     44#define CHT_WC_CHGRCTRL1_FTEMP_EVENT		BIT(5)
     45#define CHT_WC_CHGRCTRL1_OTGMODE		BIT(6)
     46#define CHT_WC_CHGRCTRL1_DBPEN			BIT(7)
     47
     48#define CHT_WC_USBSRC			0x5e29
     49#define CHT_WC_USBSRC_STS_MASK		GENMASK(1, 0)
     50#define CHT_WC_USBSRC_STS_SUCCESS	2
     51#define CHT_WC_USBSRC_STS_FAIL		3
     52#define CHT_WC_USBSRC_TYPE_SHIFT	2
     53#define CHT_WC_USBSRC_TYPE_MASK		GENMASK(5, 2)
     54#define CHT_WC_USBSRC_TYPE_NONE		0
     55#define CHT_WC_USBSRC_TYPE_SDP		1
     56#define CHT_WC_USBSRC_TYPE_DCP		2
     57#define CHT_WC_USBSRC_TYPE_CDP		3
     58#define CHT_WC_USBSRC_TYPE_ACA		4
     59#define CHT_WC_USBSRC_TYPE_SE1		5
     60#define CHT_WC_USBSRC_TYPE_MHL		6
     61#define CHT_WC_USBSRC_TYPE_FLOATING	7
     62#define CHT_WC_USBSRC_TYPE_OTHER	8
     63#define CHT_WC_USBSRC_TYPE_DCP_EXTPHY	9
     64
     65#define CHT_WC_CHGDISCTRL		0x5e2f
     66#define CHT_WC_CHGDISCTRL_OUT		BIT(0)
     67/* 0 - open drain, 1 - regular push-pull output */
     68#define CHT_WC_CHGDISCTRL_DRV		BIT(4)
     69/* 0 - pin is controlled by SW, 1 - by HW */
     70#define CHT_WC_CHGDISCTRL_FN		BIT(6)
     71
     72#define CHT_WC_PWRSRC_IRQ		0x6e03
     73#define CHT_WC_PWRSRC_IRQ_MASK		0x6e0f
     74#define CHT_WC_PWRSRC_STS		0x6e1e
     75#define CHT_WC_PWRSRC_VBUS		BIT(0)
     76#define CHT_WC_PWRSRC_DC		BIT(1)
     77#define CHT_WC_PWRSRC_BATT		BIT(2)
     78#define CHT_WC_PWRSRC_USBID_MASK	GENMASK(4, 3)
     79#define CHT_WC_PWRSRC_USBID_SHIFT	3
     80#define CHT_WC_PWRSRC_RID_ACA		0
     81#define CHT_WC_PWRSRC_RID_GND		1
     82#define CHT_WC_PWRSRC_RID_FLOAT		2
     83
     84#define CHT_WC_VBUS_GPIO_CTLO		0x6e2d
     85#define CHT_WC_VBUS_GPIO_CTLO_OUTPUT	BIT(0)
     86#define CHT_WC_VBUS_GPIO_CTLO_DRV_OD	BIT(4)
     87#define CHT_WC_VBUS_GPIO_CTLO_DIR_OUT	BIT(5)
     88
     89enum cht_wc_mux_select {
     90	MUX_SEL_PMIC = 0,
     91	MUX_SEL_SOC,
     92};
     93
     94static const unsigned int cht_wc_extcon_cables[] = {
     95	EXTCON_USB,
     96	EXTCON_USB_HOST,
     97	EXTCON_CHG_USB_SDP,
     98	EXTCON_CHG_USB_CDP,
     99	EXTCON_CHG_USB_DCP,
    100	EXTCON_CHG_USB_ACA,
    101	EXTCON_NONE,
    102};
    103
    104struct cht_wc_extcon_data {
    105	struct device *dev;
    106	struct regmap *regmap;
    107	struct extcon_dev *edev;
    108	struct usb_role_switch *role_sw;
    109	struct regulator *vbus_boost;
    110	struct power_supply *psy;
    111	enum power_supply_usb_type usb_type;
    112	unsigned int previous_cable;
    113	bool usb_host;
    114	bool vbus_boost_enabled;
    115};
    116
    117static int cht_wc_extcon_get_id(struct cht_wc_extcon_data *ext, int pwrsrc_sts)
    118{
    119	switch ((pwrsrc_sts & CHT_WC_PWRSRC_USBID_MASK) >> CHT_WC_PWRSRC_USBID_SHIFT) {
    120	case CHT_WC_PWRSRC_RID_GND:
    121		return INTEL_USB_ID_GND;
    122	case CHT_WC_PWRSRC_RID_FLOAT:
    123		return INTEL_USB_ID_FLOAT;
    124	/*
    125	 * According to the spec. we should read the USB-ID pin ADC value here
    126	 * to determine the resistance of the used pull-down resister and then
    127	 * return RID_A / RID_B / RID_C based on this. But all "Accessory
    128	 * Charger Adapter"s (ACAs) which users can actually buy always use
    129	 * a combination of a charging port with one or more USB-A ports, so
    130	 * they should always use a resistor indicating RID_A. But the spec
    131	 * is hard to read / badly-worded so some of them actually indicate
    132	 * they are a RID_B ACA evnen though they clearly are a RID_A ACA.
    133	 * To workaround this simply always return INTEL_USB_RID_A, which
    134	 * matches all the ACAs which users can actually buy.
    135	 */
    136	case CHT_WC_PWRSRC_RID_ACA:
    137		return INTEL_USB_RID_A;
    138	default:
    139		return INTEL_USB_ID_FLOAT;
    140	}
    141}
    142
    143static int cht_wc_extcon_get_charger(struct cht_wc_extcon_data *ext,
    144				     bool ignore_errors)
    145{
    146	int ret, usbsrc, status;
    147	unsigned long timeout;
    148
    149	/* Charger detection can take upto 600ms, wait 800ms max. */
    150	timeout = jiffies + msecs_to_jiffies(800);
    151	do {
    152		ret = regmap_read(ext->regmap, CHT_WC_USBSRC, &usbsrc);
    153		if (ret) {
    154			dev_err(ext->dev, "Error reading usbsrc: %d\n", ret);
    155			return ret;
    156		}
    157
    158		status = usbsrc & CHT_WC_USBSRC_STS_MASK;
    159		if (status == CHT_WC_USBSRC_STS_SUCCESS ||
    160		    status == CHT_WC_USBSRC_STS_FAIL)
    161			break;
    162
    163		msleep(50); /* Wait a bit before retrying */
    164	} while (time_before(jiffies, timeout));
    165
    166	if (status != CHT_WC_USBSRC_STS_SUCCESS) {
    167		if (!ignore_errors) {
    168			if (status == CHT_WC_USBSRC_STS_FAIL)
    169				dev_warn(ext->dev, "Could not detect charger type\n");
    170			else
    171				dev_warn(ext->dev, "Timeout detecting charger type\n");
    172		}
    173
    174		/* Safe fallback */
    175		usbsrc = CHT_WC_USBSRC_TYPE_SDP << CHT_WC_USBSRC_TYPE_SHIFT;
    176	}
    177
    178	usbsrc = (usbsrc & CHT_WC_USBSRC_TYPE_MASK) >> CHT_WC_USBSRC_TYPE_SHIFT;
    179	switch (usbsrc) {
    180	default:
    181		dev_warn(ext->dev,
    182			"Unhandled charger type %d, defaulting to SDP\n",
    183			 ret);
    184		ext->usb_type = POWER_SUPPLY_USB_TYPE_SDP;
    185		return EXTCON_CHG_USB_SDP;
    186	case CHT_WC_USBSRC_TYPE_SDP:
    187	case CHT_WC_USBSRC_TYPE_FLOATING:
    188	case CHT_WC_USBSRC_TYPE_OTHER:
    189		ext->usb_type = POWER_SUPPLY_USB_TYPE_SDP;
    190		return EXTCON_CHG_USB_SDP;
    191	case CHT_WC_USBSRC_TYPE_CDP:
    192		ext->usb_type = POWER_SUPPLY_USB_TYPE_CDP;
    193		return EXTCON_CHG_USB_CDP;
    194	case CHT_WC_USBSRC_TYPE_DCP:
    195	case CHT_WC_USBSRC_TYPE_DCP_EXTPHY:
    196	case CHT_WC_USBSRC_TYPE_MHL: /* MHL2+ delivers upto 2A, treat as DCP */
    197		ext->usb_type = POWER_SUPPLY_USB_TYPE_DCP;
    198		return EXTCON_CHG_USB_DCP;
    199	case CHT_WC_USBSRC_TYPE_ACA:
    200		ext->usb_type = POWER_SUPPLY_USB_TYPE_ACA;
    201		return EXTCON_CHG_USB_ACA;
    202	}
    203}
    204
    205static void cht_wc_extcon_set_phymux(struct cht_wc_extcon_data *ext, u8 state)
    206{
    207	int ret;
    208
    209	ret = regmap_write(ext->regmap, CHT_WC_PHYCTRL, state);
    210	if (ret)
    211		dev_err(ext->dev, "Error writing phyctrl: %d\n", ret);
    212}
    213
    214static void cht_wc_extcon_set_5v_boost(struct cht_wc_extcon_data *ext,
    215				       bool enable)
    216{
    217	int ret, val;
    218
    219	/*
    220	 * The 5V boost converter is enabled through a gpio on the PMIC, since
    221	 * there currently is no gpio driver we access the gpio reg directly.
    222	 */
    223	val = CHT_WC_VBUS_GPIO_CTLO_DRV_OD | CHT_WC_VBUS_GPIO_CTLO_DIR_OUT;
    224	if (enable)
    225		val |= CHT_WC_VBUS_GPIO_CTLO_OUTPUT;
    226
    227	ret = regmap_write(ext->regmap, CHT_WC_VBUS_GPIO_CTLO, val);
    228	if (ret)
    229		dev_err(ext->dev, "Error writing Vbus GPIO CTLO: %d\n", ret);
    230}
    231
    232static void cht_wc_extcon_set_otgmode(struct cht_wc_extcon_data *ext,
    233				      bool enable)
    234{
    235	unsigned int val = enable ? CHT_WC_CHGRCTRL1_OTGMODE : 0;
    236	int ret;
    237
    238	ret = regmap_update_bits(ext->regmap, CHT_WC_CHGRCTRL1,
    239				 CHT_WC_CHGRCTRL1_OTGMODE, val);
    240	if (ret)
    241		dev_err(ext->dev, "Error updating CHGRCTRL1 reg: %d\n", ret);
    242
    243	if (ext->vbus_boost && ext->vbus_boost_enabled != enable) {
    244		if (enable)
    245			ret = regulator_enable(ext->vbus_boost);
    246		else
    247			ret = regulator_disable(ext->vbus_boost);
    248
    249		if (ret)
    250			dev_err(ext->dev, "Error updating Vbus boost regulator: %d\n", ret);
    251		else
    252			ext->vbus_boost_enabled = enable;
    253	}
    254}
    255
    256static void cht_wc_extcon_enable_charging(struct cht_wc_extcon_data *ext,
    257					  bool enable)
    258{
    259	unsigned int val = enable ? 0 : CHT_WC_CHGDISCTRL_OUT;
    260	int ret;
    261
    262	ret = regmap_update_bits(ext->regmap, CHT_WC_CHGDISCTRL,
    263				 CHT_WC_CHGDISCTRL_OUT, val);
    264	if (ret)
    265		dev_err(ext->dev, "Error updating CHGDISCTRL reg: %d\n", ret);
    266}
    267
    268/* Small helper to sync EXTCON_CHG_USB_SDP and EXTCON_USB state */
    269static void cht_wc_extcon_set_state(struct cht_wc_extcon_data *ext,
    270				    unsigned int cable, bool state)
    271{
    272	extcon_set_state_sync(ext->edev, cable, state);
    273	if (cable == EXTCON_CHG_USB_SDP)
    274		extcon_set_state_sync(ext->edev, EXTCON_USB, state);
    275}
    276
    277static void cht_wc_extcon_pwrsrc_event(struct cht_wc_extcon_data *ext)
    278{
    279	int ret, pwrsrc_sts, id;
    280	unsigned int cable = EXTCON_NONE;
    281	/* Ignore errors in host mode, as the 5v boost converter is on then */
    282	bool ignore_get_charger_errors = ext->usb_host;
    283	enum usb_role role;
    284
    285	ext->usb_type = POWER_SUPPLY_USB_TYPE_UNKNOWN;
    286
    287	ret = regmap_read(ext->regmap, CHT_WC_PWRSRC_STS, &pwrsrc_sts);
    288	if (ret) {
    289		dev_err(ext->dev, "Error reading pwrsrc status: %d\n", ret);
    290		return;
    291	}
    292
    293	id = cht_wc_extcon_get_id(ext, pwrsrc_sts);
    294	if (id == INTEL_USB_ID_GND) {
    295		cht_wc_extcon_enable_charging(ext, false);
    296		cht_wc_extcon_set_otgmode(ext, true);
    297
    298		/* The 5v boost causes a false VBUS / SDP detect, skip */
    299		goto charger_det_done;
    300	}
    301
    302	cht_wc_extcon_set_otgmode(ext, false);
    303	cht_wc_extcon_enable_charging(ext, true);
    304
    305	/* Plugged into a host/charger or not connected? */
    306	if (!(pwrsrc_sts & CHT_WC_PWRSRC_VBUS)) {
    307		/* Route D+ and D- to PMIC for future charger detection */
    308		cht_wc_extcon_set_phymux(ext, MUX_SEL_PMIC);
    309		goto set_state;
    310	}
    311
    312	ret = cht_wc_extcon_get_charger(ext, ignore_get_charger_errors);
    313	if (ret >= 0)
    314		cable = ret;
    315
    316charger_det_done:
    317	/* Route D+ and D- to SoC for the host or gadget controller */
    318	cht_wc_extcon_set_phymux(ext, MUX_SEL_SOC);
    319
    320set_state:
    321	if (cable != ext->previous_cable) {
    322		cht_wc_extcon_set_state(ext, cable, true);
    323		cht_wc_extcon_set_state(ext, ext->previous_cable, false);
    324		ext->previous_cable = cable;
    325	}
    326
    327	ext->usb_host = ((id == INTEL_USB_ID_GND) || (id == INTEL_USB_RID_A));
    328	extcon_set_state_sync(ext->edev, EXTCON_USB_HOST, ext->usb_host);
    329
    330	if (ext->usb_host)
    331		role = USB_ROLE_HOST;
    332	else if (pwrsrc_sts & CHT_WC_PWRSRC_VBUS)
    333		role = USB_ROLE_DEVICE;
    334	else
    335		role = USB_ROLE_NONE;
    336
    337	/* Note: this is a no-op when ext->role_sw is NULL */
    338	ret = usb_role_switch_set_role(ext->role_sw, role);
    339	if (ret)
    340		dev_err(ext->dev, "Error setting USB-role: %d\n", ret);
    341
    342	if (ext->psy)
    343		power_supply_changed(ext->psy);
    344}
    345
    346static irqreturn_t cht_wc_extcon_isr(int irq, void *data)
    347{
    348	struct cht_wc_extcon_data *ext = data;
    349	int ret, irqs;
    350
    351	ret = regmap_read(ext->regmap, CHT_WC_PWRSRC_IRQ, &irqs);
    352	if (ret) {
    353		dev_err(ext->dev, "Error reading irqs: %d\n", ret);
    354		return IRQ_NONE;
    355	}
    356
    357	cht_wc_extcon_pwrsrc_event(ext);
    358
    359	ret = regmap_write(ext->regmap, CHT_WC_PWRSRC_IRQ, irqs);
    360	if (ret) {
    361		dev_err(ext->dev, "Error writing irqs: %d\n", ret);
    362		return IRQ_NONE;
    363	}
    364
    365	return IRQ_HANDLED;
    366}
    367
    368static int cht_wc_extcon_sw_control(struct cht_wc_extcon_data *ext, bool enable)
    369{
    370	int ret, mask, val;
    371
    372	val = enable ? 0 : CHT_WC_CHGDISCTRL_FN;
    373	ret = regmap_update_bits(ext->regmap, CHT_WC_CHGDISCTRL,
    374				 CHT_WC_CHGDISCTRL_FN, val);
    375	if (ret)
    376		dev_err(ext->dev,
    377			"Error setting sw control for CHGDIS pin: %d\n",
    378			ret);
    379
    380	mask = CHT_WC_CHGRCTRL0_SWCONTROL | CHT_WC_CHGRCTRL0_CCSM_OFF;
    381	val = enable ? mask : 0;
    382	ret = regmap_update_bits(ext->regmap, CHT_WC_CHGRCTRL0, mask, val);
    383	if (ret)
    384		dev_err(ext->dev, "Error setting sw control: %d\n", ret);
    385
    386	return ret;
    387}
    388
    389static int cht_wc_extcon_find_role_sw(struct cht_wc_extcon_data *ext)
    390{
    391	const struct software_node *swnode;
    392	struct fwnode_handle *fwnode;
    393
    394	swnode = software_node_find_by_name(NULL, "intel-xhci-usb-sw");
    395	if (!swnode)
    396		return -EPROBE_DEFER;
    397
    398	fwnode = software_node_fwnode(swnode);
    399	ext->role_sw = usb_role_switch_find_by_fwnode(fwnode);
    400	fwnode_handle_put(fwnode);
    401
    402	return ext->role_sw ? 0 : -EPROBE_DEFER;
    403}
    404
    405static void cht_wc_extcon_put_role_sw(void *data)
    406{
    407	struct cht_wc_extcon_data *ext = data;
    408
    409	usb_role_switch_put(ext->role_sw);
    410}
    411
    412/* Some boards require controlling the role-sw and Vbus based on the id-pin */
    413static int cht_wc_extcon_get_role_sw_and_regulator(struct cht_wc_extcon_data *ext)
    414{
    415	int ret;
    416
    417	ret = cht_wc_extcon_find_role_sw(ext);
    418	if (ret)
    419		return ret;
    420
    421	ret = devm_add_action_or_reset(ext->dev, cht_wc_extcon_put_role_sw, ext);
    422	if (ret)
    423		return ret;
    424
    425	/*
    426	 * On x86/ACPI platforms the regulator <-> consumer link is provided
    427	 * by platform_data passed to the regulator driver. This means that
    428	 * this info is not available before the regulator driver has bound.
    429	 * Use devm_regulator_get_optional() to avoid getting a dummy
    430	 * regulator and wait for the regulator to show up if necessary.
    431	 */
    432	ext->vbus_boost = devm_regulator_get_optional(ext->dev, "vbus");
    433	if (IS_ERR(ext->vbus_boost)) {
    434		ret = PTR_ERR(ext->vbus_boost);
    435		if (ret == -ENODEV)
    436			ret = -EPROBE_DEFER;
    437
    438		return dev_err_probe(ext->dev, ret, "getting Vbus regulator");
    439	}
    440
    441	return 0;
    442}
    443
    444static int cht_wc_extcon_psy_get_prop(struct power_supply *psy,
    445				      enum power_supply_property psp,
    446				      union power_supply_propval *val)
    447{
    448	struct cht_wc_extcon_data *ext = power_supply_get_drvdata(psy);
    449
    450	switch (psp) {
    451	case POWER_SUPPLY_PROP_USB_TYPE:
    452		val->intval = ext->usb_type;
    453		break;
    454	case POWER_SUPPLY_PROP_ONLINE:
    455		val->intval = ext->usb_type ? 1 : 0;
    456		break;
    457	default:
    458		return -EINVAL;
    459	}
    460
    461	return 0;
    462}
    463
    464static const enum power_supply_usb_type cht_wc_extcon_psy_usb_types[] = {
    465	POWER_SUPPLY_USB_TYPE_SDP,
    466	POWER_SUPPLY_USB_TYPE_CDP,
    467	POWER_SUPPLY_USB_TYPE_DCP,
    468	POWER_SUPPLY_USB_TYPE_ACA,
    469	POWER_SUPPLY_USB_TYPE_UNKNOWN,
    470};
    471
    472static const enum power_supply_property cht_wc_extcon_psy_props[] = {
    473	POWER_SUPPLY_PROP_USB_TYPE,
    474	POWER_SUPPLY_PROP_ONLINE,
    475};
    476
    477static const struct power_supply_desc cht_wc_extcon_psy_desc = {
    478	.name = "cht_wcove_pwrsrc",
    479	.type = POWER_SUPPLY_TYPE_USB,
    480	.usb_types = cht_wc_extcon_psy_usb_types,
    481	.num_usb_types = ARRAY_SIZE(cht_wc_extcon_psy_usb_types),
    482	.properties = cht_wc_extcon_psy_props,
    483	.num_properties = ARRAY_SIZE(cht_wc_extcon_psy_props),
    484	.get_property = cht_wc_extcon_psy_get_prop,
    485};
    486
    487static int cht_wc_extcon_register_psy(struct cht_wc_extcon_data *ext)
    488{
    489	struct power_supply_config psy_cfg = { .drv_data = ext };
    490
    491	ext->psy = devm_power_supply_register(ext->dev,
    492					      &cht_wc_extcon_psy_desc,
    493					      &psy_cfg);
    494	return PTR_ERR_OR_ZERO(ext->psy);
    495}
    496
    497static int cht_wc_extcon_probe(struct platform_device *pdev)
    498{
    499	struct intel_soc_pmic *pmic = dev_get_drvdata(pdev->dev.parent);
    500	struct cht_wc_extcon_data *ext;
    501	unsigned long mask = ~(CHT_WC_PWRSRC_VBUS | CHT_WC_PWRSRC_USBID_MASK);
    502	int pwrsrc_sts, id;
    503	int irq, ret;
    504
    505	irq = platform_get_irq(pdev, 0);
    506	if (irq < 0)
    507		return irq;
    508
    509	ext = devm_kzalloc(&pdev->dev, sizeof(*ext), GFP_KERNEL);
    510	if (!ext)
    511		return -ENOMEM;
    512
    513	ext->dev = &pdev->dev;
    514	ext->regmap = pmic->regmap;
    515	ext->previous_cable = EXTCON_NONE;
    516
    517	/* Initialize extcon device */
    518	ext->edev = devm_extcon_dev_allocate(ext->dev, cht_wc_extcon_cables);
    519	if (IS_ERR(ext->edev))
    520		return PTR_ERR(ext->edev);
    521
    522	switch (pmic->cht_wc_model) {
    523	case INTEL_CHT_WC_GPD_WIN_POCKET:
    524		/*
    525		 * When a host-cable is detected the BIOS enables an external 5v boost
    526		 * converter to power connected devices there are 2 problems with this:
    527		 * 1) This gets seen by the external battery charger as a valid Vbus
    528		 *    supply and it then tries to feed Vsys from this creating a
    529		 *    feedback loop which causes aprox. 300 mA extra battery drain
    530		 *    (and unless we drive the external-charger-disable pin high it
    531		 *    also tries to charge the battery causing even more feedback).
    532		 * 2) This gets seen by the pwrsrc block as a SDP USB Vbus supply
    533		 * Since the external battery charger has its own 5v boost converter
    534		 * which does not have these issues, we simply turn the separate
    535		 * external 5v boost converter off and leave it off entirely.
    536		 */
    537		cht_wc_extcon_set_5v_boost(ext, false);
    538		break;
    539	case INTEL_CHT_WC_LENOVO_YOGABOOK1:
    540		/* Do this first, as it may very well return -EPROBE_DEFER. */
    541		ret = cht_wc_extcon_get_role_sw_and_regulator(ext);
    542		if (ret)
    543			return ret;
    544		/*
    545		 * The bq25890 used here relies on this driver's BC-1.2 charger
    546		 * detection, and the bq25890 driver expect this info to be
    547		 * available through a parent power_supply class device which
    548		 * models the detected charger (idem to how the Type-C TCPM code
    549		 * registers a power_supply classdev for the connected charger).
    550		 */
    551		ret = cht_wc_extcon_register_psy(ext);
    552		if (ret)
    553			return ret;
    554		break;
    555	case INTEL_CHT_WC_XIAOMI_MIPAD2:
    556		ret = cht_wc_extcon_get_role_sw_and_regulator(ext);
    557		if (ret)
    558			return ret;
    559		break;
    560	default:
    561		break;
    562	}
    563
    564	/* Enable sw control */
    565	ret = cht_wc_extcon_sw_control(ext, true);
    566	if (ret)
    567		goto disable_sw_control;
    568
    569	/* Disable charging by external battery charger */
    570	cht_wc_extcon_enable_charging(ext, false);
    571
    572	/* Register extcon device */
    573	ret = devm_extcon_dev_register(ext->dev, ext->edev);
    574	if (ret) {
    575		dev_err(ext->dev, "Error registering extcon device: %d\n", ret);
    576		goto disable_sw_control;
    577	}
    578
    579	ret = regmap_read(ext->regmap, CHT_WC_PWRSRC_STS, &pwrsrc_sts);
    580	if (ret) {
    581		dev_err(ext->dev, "Error reading pwrsrc status: %d\n", ret);
    582		goto disable_sw_control;
    583	}
    584
    585	/*
    586	 * If no USB host or device connected, route D+ and D- to PMIC for
    587	 * initial charger detection
    588	 */
    589	id = cht_wc_extcon_get_id(ext, pwrsrc_sts);
    590	if (id != INTEL_USB_ID_GND)
    591		cht_wc_extcon_set_phymux(ext, MUX_SEL_PMIC);
    592
    593	/* Get initial state */
    594	cht_wc_extcon_pwrsrc_event(ext);
    595
    596	ret = devm_request_threaded_irq(ext->dev, irq, NULL, cht_wc_extcon_isr,
    597					IRQF_ONESHOT, pdev->name, ext);
    598	if (ret) {
    599		dev_err(ext->dev, "Error requesting interrupt: %d\n", ret);
    600		goto disable_sw_control;
    601	}
    602
    603	/* Unmask irqs */
    604	ret = regmap_write(ext->regmap, CHT_WC_PWRSRC_IRQ_MASK, mask);
    605	if (ret) {
    606		dev_err(ext->dev, "Error writing irq-mask: %d\n", ret);
    607		goto disable_sw_control;
    608	}
    609
    610	platform_set_drvdata(pdev, ext);
    611
    612	return 0;
    613
    614disable_sw_control:
    615	cht_wc_extcon_sw_control(ext, false);
    616	return ret;
    617}
    618
    619static int cht_wc_extcon_remove(struct platform_device *pdev)
    620{
    621	struct cht_wc_extcon_data *ext = platform_get_drvdata(pdev);
    622
    623	cht_wc_extcon_sw_control(ext, false);
    624
    625	return 0;
    626}
    627
    628static const struct platform_device_id cht_wc_extcon_table[] = {
    629	{ .name = "cht_wcove_pwrsrc" },
    630	{},
    631};
    632MODULE_DEVICE_TABLE(platform, cht_wc_extcon_table);
    633
    634static struct platform_driver cht_wc_extcon_driver = {
    635	.probe = cht_wc_extcon_probe,
    636	.remove = cht_wc_extcon_remove,
    637	.id_table = cht_wc_extcon_table,
    638	.driver = {
    639		.name = "cht_wcove_pwrsrc",
    640	},
    641};
    642module_platform_driver(cht_wc_extcon_driver);
    643
    644MODULE_DESCRIPTION("Intel Cherrytrail Whiskey Cove PMIC extcon driver");
    645MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
    646MODULE_LICENSE("GPL v2");