0001
0002
0003
0004
0005
0006
0007
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
0068 #define CHT_WC_CHGDISCTRL_DRV BIT(4)
0069
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
0126
0127
0128
0129
0130
0131
0132
0133
0134
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
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);
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
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:
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
0221
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
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
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
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
0306 if (!(pwrsrc_sts & CHT_WC_PWRSRC_VBUS)) {
0307
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
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
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
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
0427
0428
0429
0430
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
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
0526
0527
0528
0529
0530
0531
0532
0533
0534
0535
0536
0537 cht_wc_extcon_set_5v_boost(ext, false);
0538 break;
0539 case INTEL_CHT_WC_LENOVO_YOGABOOK1:
0540
0541 ret = cht_wc_extcon_get_role_sw_and_regulator(ext);
0542 if (ret)
0543 return ret;
0544
0545
0546
0547
0548
0549
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
0565 ret = cht_wc_extcon_sw_control(ext, true);
0566 if (ret)
0567 goto disable_sw_control;
0568
0569
0570 cht_wc_extcon_enable_charging(ext, false);
0571
0572
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
0587
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
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
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");