Lines Matching +full:usb +full:- +full:sdp

1 // SPDX-License-Identifier: GPL-2.0-only
3 * axp288_charger.c - X-power AXP288 PMIC Charger driver
5 * Copyright (C) 2016-2017 Hans de Goede <hdegoede@redhat.com>
18 #include <linux/usb/otg.h>
134 /* SDP/CDP/DCP USB charging cable notifications */
160 else if (cc > info->max_cc) in axp288_charger_set_cc()
161 cc = info->max_cc; in axp288_charger_set_cc()
163 reg_val = (cc - CHRG_CCCV_CC_OFFSET) / CHRG_CCCV_CC_LSB_RES; in axp288_charger_set_cc()
167 ret = regmap_update_bits(info->regmap, in axp288_charger_set_cc()
171 info->cc = cc; in axp288_charger_set_cc()
197 ret = regmap_update_bits(info->regmap, in axp288_charger_set_cv()
202 info->cv = cv; in axp288_charger_set_cv()
211 val = info->backend_control; in axp288_charger_get_vbus_inlmt()
262 ret = regmap_update_bits(info->regmap, AXP20X_CHRG_BAK_CTRL, in axp288_charger_set_vbus_inlmt()
265 dev_err(&info->pdev->dev, "charger BAK control %d\n", ret); in axp288_charger_set_vbus_inlmt()
276 ret = regmap_update_bits(info->regmap, AXP20X_VBUS_IPSOUT_MGMT, in axp288_charger_vbus_path_select()
279 ret = regmap_update_bits(info->regmap, AXP20X_VBUS_IPSOUT_MGMT, in axp288_charger_vbus_path_select()
283 dev_err(&info->pdev->dev, "axp288 vbus path select %d\n", ret); in axp288_charger_vbus_path_select()
294 ret = regmap_update_bits(info->regmap, AXP20X_CHRG_CTRL1, in axp288_charger_enable_charger()
297 ret = regmap_update_bits(info->regmap, AXP20X_CHRG_CTRL1, in axp288_charger_enable_charger()
300 dev_err(&info->pdev->dev, "axp288 enable charger %d\n", ret); in axp288_charger_enable_charger()
307 if (!(info->input_status & PS_STAT_VBUS_PRESENT)) in axp288_get_charger_health()
310 if (!(info->input_status & PS_STAT_VBUS_VALID)) in axp288_get_charger_health()
312 else if (info->op_mode & CHRG_STAT_PMIC_OTP) in axp288_get_charger_health()
314 else if (info->op_mode & CHRG_STAT_BAT_SAFE_MODE) in axp288_get_charger_health()
328 mutex_lock(&info->lock); in axp288_charger_usb_set_property()
331 scaled_val = min(val->intval, info->max_cc); in axp288_charger_usb_set_property()
335 dev_warn(&info->pdev->dev, "set charge current failed\n"); in axp288_charger_usb_set_property()
340 scaled_val = DIV_ROUND_CLOSEST(val->intval, 1000); in axp288_charger_usb_set_property()
341 scaled_val = min(scaled_val, info->max_cv); in axp288_charger_usb_set_property()
344 dev_warn(&info->pdev->dev, "set charge voltage failed\n"); in axp288_charger_usb_set_property()
349 ret = axp288_charger_set_vbus_inlmt(info, val->intval); in axp288_charger_usb_set_property()
351 dev_warn(&info->pdev->dev, "set input current limit failed\n"); in axp288_charger_usb_set_property()
354 info->valid = false; in axp288_charger_usb_set_property()
357 ret = -EINVAL; in axp288_charger_usb_set_property()
361 mutex_unlock(&info->lock); in axp288_charger_usb_set_property()
369 ret = regmap_read(info->regmap, reg, ret_val); in axp288_charger_reg_readb()
371 dev_err(&info->pdev->dev, "Error %d on reading value from register 0x%04x\n", in axp288_charger_reg_readb()
383 if (info->valid && time_before(jiffies, info->last_updated + AXP288_REG_UPDATE_INTERVAL)) in axp288_charger_usb_update_property()
386 dev_dbg(&info->pdev->dev, "Charger updating register values...\n"); in axp288_charger_usb_update_property()
392 ret = axp288_charger_reg_readb(info, AXP20X_PWR_INPUT_STATUS, &info->input_status); in axp288_charger_usb_update_property()
396 ret = axp288_charger_reg_readb(info, AXP20X_PWR_OP_MODE, &info->op_mode); in axp288_charger_usb_update_property()
400 ret = axp288_charger_reg_readb(info, AXP20X_CHRG_BAK_CTRL, &info->backend_control); in axp288_charger_usb_update_property()
404 info->last_updated = jiffies; in axp288_charger_usb_update_property()
405 info->valid = true; in axp288_charger_usb_update_property()
418 mutex_lock(&info->lock); in axp288_charger_usb_get_property()
426 if (info->otg.id_short) { in axp288_charger_usb_get_property()
427 val->intval = 0; in axp288_charger_usb_get_property()
430 val->intval = (info->input_status & PS_STAT_VBUS_PRESENT) ? 1 : 0; in axp288_charger_usb_get_property()
434 if (info->otg.id_short) { in axp288_charger_usb_get_property()
435 val->intval = 0; in axp288_charger_usb_get_property()
438 val->intval = (info->input_status & PS_STAT_VBUS_VALID) ? 1 : 0; in axp288_charger_usb_get_property()
441 val->intval = axp288_get_charger_health(info); in axp288_charger_usb_get_property()
444 val->intval = info->cc * 1000; in axp288_charger_usb_get_property()
447 val->intval = info->max_cc * 1000; in axp288_charger_usb_get_property()
450 val->intval = info->cv * 1000; in axp288_charger_usb_get_property()
453 val->intval = info->max_cv * 1000; in axp288_charger_usb_get_property()
456 val->intval = axp288_charger_get_vbus_inlmt(info); in axp288_charger_usb_get_property()
459 ret = -EINVAL; in axp288_charger_usb_get_property()
463 mutex_unlock(&info->lock); in axp288_charger_usb_get_property()
513 if (info->irq[i] == irq) in axp288_charger_irq_thread_handler()
518 dev_warn(&info->pdev->dev, "spurious interrupt!!\n"); in axp288_charger_irq_thread_handler()
524 dev_dbg(&info->pdev->dev, "VBUS Over Voltage INTR\n"); in axp288_charger_irq_thread_handler()
527 dev_dbg(&info->pdev->dev, "Charging Done INTR\n"); in axp288_charger_irq_thread_handler()
530 dev_dbg(&info->pdev->dev, "Start Charging IRQ\n"); in axp288_charger_irq_thread_handler()
533 dev_dbg(&info->pdev->dev, in axp288_charger_irq_thread_handler()
537 dev_dbg(&info->pdev->dev, in axp288_charger_irq_thread_handler()
541 dev_dbg(&info->pdev->dev, in axp288_charger_irq_thread_handler()
545 dev_dbg(&info->pdev->dev, in axp288_charger_irq_thread_handler()
549 dev_dbg(&info->pdev->dev, in axp288_charger_irq_thread_handler()
553 dev_dbg(&info->pdev->dev, in axp288_charger_irq_thread_handler()
557 dev_warn(&info->pdev->dev, "Spurious Interrupt!!!\n"); in axp288_charger_irq_thread_handler()
560 mutex_lock(&info->lock); in axp288_charger_irq_thread_handler()
561 info->valid = false; in axp288_charger_irq_thread_handler()
562 mutex_unlock(&info->lock); in axp288_charger_irq_thread_handler()
563 power_supply_changed(info->psy_usb); in axp288_charger_irq_thread_handler()
570 * Bay Trail SoC + AXP288 PMIC, Micro-USB, DMI_BOARD_NAME: "8021"
571 * Bay Trail SoC + AXP288 PMIC, Type-C, DMI_BOARD_NAME: "815D"
572 * Cherry Trail SoC + AXP288 PMIC, Type-C, DMI_BOARD_NAME: "813E"
573 * Cherry Trail SoC + TI PMIC, Type-C, DMI_BOARD_NAME: "827C" or "82F4"
575 * The variants with the AXP288 + Type-C connector are all kinds of special:
577 * 1. They use a Type-C connector which the AXP288 does not support, so when
578 * using a Type-C charger it is not recognized. Unlike most AXP288 devices,
585 * 2. If no charger is connected the machine boots with the vbus-path disabled.
588 * an OTG host cable is inserted and the ID pin on the micro-B receptacle is
590 * which re-enables the vbus-path when the ID pin is pulled high when the
591 * OTG host cable is removed. The Type-C connector has no ID pin, there is
593 * end up not charging because the vbus-path is disabled, until we unplug
594 * the charger which automatically clears the vbus-path disable bit and then
595 * on the second plug-in of the adapter we start charging. To solve the not
596 * charging on first charger plugin we unconditionally enable the vbus-path at
602 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
622 struct extcon_dev *edev = info->cable.edev; in axp288_charger_extcon_evt_worker()
625 ret = regmap_read(info->regmap, AXP20X_PWR_INPUT_STATUS, &val); in axp288_charger_extcon_evt_worker()
627 dev_err(&info->pdev->dev, "Error reading status (%d)\n", ret); in axp288_charger_extcon_evt_worker()
633 dev_dbg(&info->pdev->dev, "USB charger disconnected\n"); in axp288_charger_extcon_evt_worker()
635 mutex_lock(&info->lock); in axp288_charger_extcon_evt_worker()
636 info->valid = false; in axp288_charger_extcon_evt_worker()
637 mutex_unlock(&info->lock); in axp288_charger_extcon_evt_worker()
638 power_supply_changed(info->psy_usb); in axp288_charger_extcon_evt_worker()
645 dev_dbg(&info->pdev->dev, "HP X2 with Type-C, setting inlmt to 3A\n"); in axp288_charger_extcon_evt_worker()
648 dev_dbg(&info->pdev->dev, "USB SDP charger is connected\n"); in axp288_charger_extcon_evt_worker()
651 dev_dbg(&info->pdev->dev, "USB CDP charger is connected\n"); in axp288_charger_extcon_evt_worker()
654 dev_dbg(&info->pdev->dev, "USB DCP charger is connected\n"); in axp288_charger_extcon_evt_worker()
666 dev_err(&info->pdev->dev, in axp288_charger_extcon_evt_worker()
669 mutex_lock(&info->lock); in axp288_charger_extcon_evt_worker()
670 info->valid = false; in axp288_charger_extcon_evt_worker()
671 mutex_unlock(&info->lock); in axp288_charger_extcon_evt_worker()
672 power_supply_changed(info->psy_usb); in axp288_charger_extcon_evt_worker()
680 schedule_work(&info->cable.work); in axp288_charger_handle_cable_evt()
688 struct extcon_dev *edev = info->otg.cable; in axp288_charger_otg_evt_worker()
691 dev_dbg(&info->pdev->dev, "external connector USB-Host is %s\n", in axp288_charger_otg_evt_worker()
696 * in case usb host. in axp288_charger_otg_evt_worker()
698 info->otg.id_short = usb_host; in axp288_charger_otg_evt_worker()
701 ret = axp288_charger_vbus_path_select(info, !info->otg.id_short); in axp288_charger_otg_evt_worker()
703 dev_warn(&info->pdev->dev, "vbus path disable failed\n"); in axp288_charger_otg_evt_worker()
712 schedule_work(&info->otg.work); in axp288_charger_handle_otg_evt()
723 ret = regmap_write(info->regmap, AXP20X_V_LTF_CHRG, CHRG_VLTFC_0C); in charger_init_hw_regs()
725 dev_err(&info->pdev->dev, "register(%x) write error(%d)\n", in charger_init_hw_regs()
730 ret = regmap_write(info->regmap, AXP20X_V_HTF_CHRG, CHRG_VHTFC_45C); in charger_init_hw_regs()
732 dev_err(&info->pdev->dev, "register(%x) write error(%d)\n", in charger_init_hw_regs()
737 /* Do not turn-off charger o/p after charge cycle ends */ in charger_init_hw_regs()
738 ret = regmap_update_bits(info->regmap, in charger_init_hw_regs()
742 dev_err(&info->pdev->dev, "register(%x) write error(%d)\n", in charger_init_hw_regs()
748 ret = regmap_update_bits(info->regmap, in charger_init_hw_regs()
752 dev_err(&info->pdev->dev, "register(%x) write error(%d)\n", in charger_init_hw_regs()
757 /* Disable OCV-SOC curve calibration */ in charger_init_hw_regs()
758 ret = regmap_update_bits(info->regmap, in charger_init_hw_regs()
762 dev_err(&info->pdev->dev, "register(%x) write error(%d)\n", in charger_init_hw_regs()
775 ret = regmap_update_bits(info->regmap, AXP20X_VBUS_IPSOUT_MGMT, in charger_init_hw_regs()
778 dev_err(&info->pdev->dev, "register(%x) write error(%d)\n", in charger_init_hw_regs()
785 ret = regmap_read(info->regmap, AXP20X_CHRG_CTRL1, &val); in charger_init_hw_regs()
787 dev_err(&info->pdev->dev, "register(%x) read error(%d)\n", in charger_init_hw_regs()
796 info->cv = CV_4100MV; in charger_init_hw_regs()
799 info->cv = CV_4150MV; in charger_init_hw_regs()
802 info->cv = CV_4200MV; in charger_init_hw_regs()
805 info->cv = CV_4350MV; in charger_init_hw_regs()
812 info->cc = cc; in charger_init_hw_regs()
818 info->max_cv = info->cv; in charger_init_hw_regs()
819 info->max_cc = info->cc; in charger_init_hw_regs()
828 cancel_work_sync(&info->otg.work); in axp288_charger_cancel_work()
829 cancel_work_sync(&info->cable.work); in axp288_charger_cancel_work()
836 struct device *dev = &pdev->dev; in axp288_charger_probe()
837 struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); in axp288_charger_probe()
847 return -ENODEV; in axp288_charger_probe()
853 ret = regmap_read(axp20x->regmap, AXP20X_CC_CTRL, &val); in axp288_charger_probe()
857 return -ENODEV; in axp288_charger_probe()
861 return -ENOMEM; in axp288_charger_probe()
863 mutex_init(&info->lock); in axp288_charger_probe()
864 info->pdev = pdev; in axp288_charger_probe()
865 info->regmap = axp20x->regmap; in axp288_charger_probe()
866 info->regmap_irqc = axp20x->regmap_irqc; in axp288_charger_probe()
868 info->cable.edev = extcon_get_extcon_dev(AXP288_EXTCON_DEV_NAME); in axp288_charger_probe()
869 if (IS_ERR(info->cable.edev)) { in axp288_charger_probe()
870 dev_err_probe(dev, PTR_ERR(info->cable.edev), in axp288_charger_probe()
873 return PTR_ERR(info->cable.edev); in axp288_charger_probe()
878 * "INT3496" (USB_HOST_EXTCON_HID) device. x86-android-tablets.ko in axp288_charger_probe()
879 * instantiates an "intel-int3496" extcon on these devs as a workaround. in axp288_charger_probe()
882 extcon_name = "intel-int3496"; in axp288_charger_probe()
883 else if (acpi_dev_present(USB_HOST_EXTCON_HID, NULL, -1)) in axp288_charger_probe()
887 info->otg.cable = extcon_get_extcon_dev(extcon_name); in axp288_charger_probe()
888 if (IS_ERR(info->otg.cable)) { in axp288_charger_probe()
889 dev_err_probe(dev, PTR_ERR(info->otg.cable), in axp288_charger_probe()
892 return PTR_ERR(info->otg.cable); in axp288_charger_probe()
894 dev_info(dev, "Using " USB_HOST_EXTCON_HID " extcon for usb-id\n"); in axp288_charger_probe()
905 info->psy_usb = devm_power_supply_register(dev, &axp288_charger_desc, in axp288_charger_probe()
907 if (IS_ERR(info->psy_usb)) { in axp288_charger_probe()
908 ret = PTR_ERR(info->psy_usb); in axp288_charger_probe()
919 INIT_WORK(&info->cable.work, axp288_charger_extcon_evt_worker); in axp288_charger_probe()
920 info->cable.nb.notifier_call = axp288_charger_handle_cable_evt; in axp288_charger_probe()
921 ret = devm_extcon_register_notifier_all(dev, info->cable.edev, in axp288_charger_probe()
922 &info->cable.nb); in axp288_charger_probe()
927 schedule_work(&info->cable.work); in axp288_charger_probe()
930 INIT_WORK(&info->otg.work, axp288_charger_otg_evt_worker); in axp288_charger_probe()
931 info->otg.id_nb.notifier_call = axp288_charger_handle_otg_evt; in axp288_charger_probe()
932 if (info->otg.cable) { in axp288_charger_probe()
933 ret = devm_extcon_register_notifier(dev, info->otg.cable, in axp288_charger_probe()
934 EXTCON_USB_HOST, &info->otg.id_nb); in axp288_charger_probe()
939 schedule_work(&info->otg.work); in axp288_charger_probe()
944 pirq = platform_get_irq(info->pdev, i); in axp288_charger_probe()
948 info->irq[i] = regmap_irq_get_virq(info->regmap_irqc, pirq); in axp288_charger_probe()
949 if (info->irq[i] < 0) { in axp288_charger_probe()
950 dev_warn(&info->pdev->dev, in axp288_charger_probe()
952 return info->irq[i]; in axp288_charger_probe()
954 ret = devm_request_threaded_irq(&info->pdev->dev, info->irq[i], in axp288_charger_probe()
956 IRQF_ONESHOT, info->pdev->name, info); in axp288_charger_probe()
959 info->irq[i]); in axp288_charger_probe()
984 MODULE_DESCRIPTION("X-power AXP288 Charger Driver");