Lines Matching +full:usb +full:- +full:role +full:- +full:switch
2 * extcon-axp288.c - X-Power AXP288 PMIC extcon cable detection driver
4 * Copyright (c) 2017-2018 Hans de Goede <hdegoede@redhat.com>
27 #include <linux/extcon-provider.h>
30 #include <linux/usb/role.h>
34 #include <asm/intel-family.h>
60 /* BC USB status register */
145 ret = regmap_read(info->regmap, AXP288_PS_BOOT_REASON_REG, &val); in axp288_extcon_log_rsi()
148 dev_dbg(info->dev, "%s\n", *rsi); in axp288_extcon_log_rsi()
154 regmap_write(info->regmap, AXP288_PS_BOOT_REASON_REG, clear_mask); in axp288_extcon_log_rsi()
158 * The below code to control the USB role-switch on devices with an AXP288
160 * to control the USB role-switch on such devices:
161 * 1) On many devices the USB role is controlled by AML code, but the AML code
166 * 2) In order for our BC1.2 charger detection to work properly the role
170 /* Returns the id-pin value, note pulled low / false == host-mode */
173 enum usb_role role; in axp288_get_id_pin() local
175 if (info->id_extcon) in axp288_get_id_pin()
176 return extcon_get_state(info->id_extcon, EXTCON_USB_HOST) <= 0; in axp288_get_id_pin()
178 /* We cannot access the id-pin, see what mode the AML code has set */ in axp288_get_id_pin()
179 role = usb_role_switch_get_role(info->role_sw); in axp288_get_id_pin()
180 return role != USB_ROLE_HOST; in axp288_get_id_pin()
187 enum usb_role role; in axp288_usb_role_work() local
193 role = USB_ROLE_HOST; in axp288_usb_role_work()
194 else if (info->vbus_attach) in axp288_usb_role_work()
195 role = USB_ROLE_DEVICE; in axp288_usb_role_work()
197 role = USB_ROLE_NONE; in axp288_usb_role_work()
199 ret = usb_role_switch_set_role(info->role_sw, role); in axp288_usb_role_work()
201 dev_err(info->dev, "failed to set role: %d\n", ret); in axp288_usb_role_work()
208 ret = regmap_read(info->regmap, AXP288_PS_STAT_REG, &pwr_stat); in axp288_get_vbus_attach()
210 dev_err(info->dev, "failed to read vbus status\n"); in axp288_get_vbus_attach()
221 unsigned int cable = info->previous_cable; in axp288_handle_chrg_det_event()
229 ret = regmap_read(info->regmap, AXP288_BC_GLOBAL_REG, &cfg); in axp288_handle_chrg_det_event()
233 dev_dbg(info->dev, "can't complete the charger detection\n"); in axp288_handle_chrg_det_event()
237 ret = regmap_read(info->regmap, AXP288_BC_DET_STAT_REG, &stat); in axp288_handle_chrg_det_event()
243 switch (chrg_type) { in axp288_handle_chrg_det_event()
245 dev_dbg(info->dev, "sdp cable is connected\n"); in axp288_handle_chrg_det_event()
249 dev_dbg(info->dev, "cdp cable is connected\n"); in axp288_handle_chrg_det_event()
253 dev_dbg(info->dev, "dcp cable is connected\n"); in axp288_handle_chrg_det_event()
257 dev_warn(info->dev, "unknown (reserved) bc detect result\n"); in axp288_handle_chrg_det_event()
262 extcon_set_state_sync(info->edev, info->previous_cable, false); in axp288_handle_chrg_det_event()
263 if (info->previous_cable == EXTCON_CHG_USB_SDP) in axp288_handle_chrg_det_event()
264 extcon_set_state_sync(info->edev, EXTCON_USB, false); in axp288_handle_chrg_det_event()
267 extcon_set_state_sync(info->edev, cable, vbus_attach); in axp288_handle_chrg_det_event()
269 extcon_set_state_sync(info->edev, EXTCON_USB, in axp288_handle_chrg_det_event()
272 info->previous_cable = cable; in axp288_handle_chrg_det_event()
275 if (info->role_sw && info->vbus_attach != vbus_attach) { in axp288_handle_chrg_det_event()
276 info->vbus_attach = vbus_attach; in axp288_handle_chrg_det_event()
277 /* Setting the role can take a while */ in axp288_handle_chrg_det_event()
278 queue_work(system_long_wq, &info->role_work); in axp288_handle_chrg_det_event()
285 dev_err(info->dev, "failed to detect BC Mod\n"); in axp288_handle_chrg_det_event()
296 /* We may not sleep and setting the role can take a while */ in axp288_extcon_id_evt()
297 queue_work(system_long_wq, &info->role_work); in axp288_extcon_id_evt()
309 dev_err(info->dev, "failed to handle the interrupt\n"); in axp288_extcon_isr()
316 regmap_update_bits(info->regmap, AXP288_BC_GLOBAL_REG, in axp288_extcon_enable()
319 regmap_update_bits(info->regmap, AXP288_BC_GLOBAL_REG, in axp288_extcon_enable()
327 cancel_work_sync(&info->role_work); in axp288_put_role_sw()
328 usb_role_switch_put(info->role_sw); in axp288_put_role_sw()
334 struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); in axp288_extcon_probe()
335 struct device *dev = &pdev->dev; in axp288_extcon_probe()
339 info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); in axp288_extcon_probe()
341 return -ENOMEM; in axp288_extcon_probe()
343 info->dev = &pdev->dev; in axp288_extcon_probe()
344 info->regmap = axp20x->regmap; in axp288_extcon_probe()
345 info->regmap_irqc = axp20x->regmap_irqc; in axp288_extcon_probe()
346 info->previous_cable = EXTCON_NONE; in axp288_extcon_probe()
347 INIT_WORK(&info->role_work, axp288_usb_role_work); in axp288_extcon_probe()
348 info->id_nb.notifier_call = axp288_extcon_id_evt; in axp288_extcon_probe()
352 info->role_sw = usb_role_switch_get(dev); in axp288_extcon_probe()
353 if (IS_ERR(info->role_sw)) in axp288_extcon_probe()
354 return PTR_ERR(info->role_sw); in axp288_extcon_probe()
355 if (info->role_sw) { in axp288_extcon_probe()
360 name = acpi_dev_get_first_match_name("INT3496", NULL, -1); in axp288_extcon_probe()
362 info->id_extcon = extcon_get_extcon_dev(name); in axp288_extcon_probe()
363 if (!info->id_extcon) in axp288_extcon_probe()
364 return -EPROBE_DEFER; in axp288_extcon_probe()
366 dev_info(dev, "controlling USB role\n"); in axp288_extcon_probe()
368 dev_info(dev, "controlling USB role based on Vbus presence\n"); in axp288_extcon_probe()
372 info->vbus_attach = axp288_get_vbus_attach(info); in axp288_extcon_probe()
377 info->edev = devm_extcon_dev_allocate(&pdev->dev, in axp288_extcon_probe()
379 if (IS_ERR(info->edev)) { in axp288_extcon_probe()
380 dev_err(&pdev->dev, "failed to allocate memory for extcon\n"); in axp288_extcon_probe()
381 return PTR_ERR(info->edev); in axp288_extcon_probe()
385 ret = devm_extcon_dev_register(&pdev->dev, info->edev); in axp288_extcon_probe()
387 dev_err(&pdev->dev, "failed to register extcon device\n"); in axp288_extcon_probe()
396 info->irq[i] = regmap_irq_get_virq(info->regmap_irqc, pirq); in axp288_extcon_probe()
397 if (info->irq[i] < 0) { in axp288_extcon_probe()
398 dev_err(&pdev->dev, in axp288_extcon_probe()
400 ret = info->irq[i]; in axp288_extcon_probe()
404 ret = devm_request_threaded_irq(&pdev->dev, info->irq[i], in axp288_extcon_probe()
407 pdev->name, info); in axp288_extcon_probe()
409 dev_err(&pdev->dev, "failed to request interrupt=%d\n", in axp288_extcon_probe()
410 info->irq[i]); in axp288_extcon_probe()
415 if (info->id_extcon) { in axp288_extcon_probe()
416 ret = devm_extcon_register_notifier_all(dev, info->id_extcon, in axp288_extcon_probe()
417 &info->id_nb); in axp288_extcon_probe()
422 /* Make sure the role-sw is set correctly before doing BC detection */ in axp288_extcon_probe()
423 if (info->role_sw) { in axp288_extcon_probe()
424 queue_work(system_long_wq, &info->role_work); in axp288_extcon_probe()
425 flush_work(&info->role_work); in axp288_extcon_probe()
442 enable_irq_wake(info->irq[VBUS_RISING_IRQ]); in axp288_extcon_suspend()
452 * Wakeup when a charger is connected to do charger-type in axp288_extcon_resume()
457 disable_irq_wake(info->irq[VBUS_RISING_IRQ]); in axp288_extcon_resume()
482 .endpoint[1] = "intel_xhci_usb_sw-role-switch",
483 .id = "usb-role-switch",
506 MODULE_DESCRIPTION("X-Powers AXP288 extcon driver");