/* BC module vbus control and status register */ #define VBUS_CNTL_DPDM_PD_EN BIT(4) #define VBUS_CNTL_DPDM_FD_EN BIT(5) #define VBUS_CNTL_FIRST_PO_STAT BIT(6)
/* Power up/down reason string array */ staticconstchar * const axp288_pwr_up_down_info[] = { "Last wake caused by user pressing the power button", "Last wake caused by a charger insertion", "Last wake caused by a battery insertion", "Last wake caused by SOC initiated global reset", "Last wake caused by cold reset", "Last shutdown caused by PMIC UVLO threshold", "Last shutdown caused by SOC initiated cold off", "Last shutdown caused by user pressing the power button",
};
/* * Decode and log the given "reset source indicator" (rsi) * register and then clear it.
*/ staticvoid axp288_extcon_log_rsi(struct axp288_extcon_info *info)
{ unsignedint val, i, clear_mask = 0; unsignedlong bits; int ret;
ret = regmap_read(info->regmap, AXP288_PS_BOOT_REASON_REG, &val); if (ret < 0) {
dev_err(info->dev, "failed to read reset source indicator\n"); return;
}
/* Clear the register value for next reboot (write 1 to clear bit) */
regmap_write(info->regmap, AXP288_PS_BOOT_REASON_REG, clear_mask);
}
/* * The below code to control the USB role-switch on devices with an AXP288 * may seem out of place, but there are 2 reasons why this is the best place * to control the USB role-switch on such devices: * 1) On many devices the USB role is controlled by AML code, but the AML code * only switches between the host and none roles, because of Windows not * really using device mode. To make device mode work we need to toggle * between the none/device roles based on Vbus presence, and this driver * gets interrupts on Vbus insertion / removal. * 2) In order for our BC1.2 charger detection to work properly the role * mux must be properly set to device mode before we do the detection.
*/
switch (chrg_type) { case DET_STAT_SDP:
dev_dbg(info->dev, "sdp cable is connected\n");
cable = EXTCON_CHG_USB_SDP; break; case DET_STAT_CDP:
dev_dbg(info->dev, "cdp cable is connected\n");
cable = EXTCON_CHG_USB_CDP; break; case DET_STAT_DCP:
dev_dbg(info->dev, "dcp cable is connected\n");
cable = EXTCON_CHG_USB_DCP; break; default:
dev_warn(info->dev, "unknown (reserved) bc detect result\n");
cable = EXTCON_CHG_USB_SDP;
}
no_vbus:
iosf_mbi_unblock_punit_i2c_access();
extcon_set_state_sync(info->edev, info->previous_cable, false); if (info->previous_cable == EXTCON_CHG_USB_SDP)
extcon_set_state_sync(info->edev, EXTCON_USB, false);
if (vbus_attach) {
extcon_set_state_sync(info->edev, cable, vbus_attach); if (cable == EXTCON_CHG_USB_SDP)
extcon_set_state_sync(info->edev, EXTCON_USB,
vbus_attach);
info->previous_cable = cable;
}
if (info->role_sw && info->vbus_attach != vbus_attach) {
info->vbus_attach = vbus_attach; /* Setting the role can take a while */
queue_work(system_long_wq, &info->role_work);
}
return 0;
dev_det_ret:
iosf_mbi_unblock_punit_i2c_access();
if (ret < 0)
dev_err(info->dev, "failed to detect BC Mod\n");
ret = axp288_extcon_find_role_sw(info); if (ret) return ret;
if (info->role_sw) {
ret = devm_add_action_or_reset(dev, axp288_put_role_sw, info); if (ret) return ret;
adev = acpi_dev_get_first_match_dev("INT3496", NULL, -1); if (adev) {
info->id_extcon = extcon_get_extcon_dev(acpi_dev_name(adev));
acpi_dev_put(adev); if (IS_ERR(info->id_extcon)) return PTR_ERR(info->id_extcon);
dev_info(dev, "controlling USB role\n");
} else {
dev_info(dev, "controlling USB role based on Vbus presence\n");
}
}
ret = iosf_mbi_block_punit_i2c_access(); if (ret < 0) return ret;
info->vbus_attach = axp288_get_vbus_attach(info);
axp288_extcon_log_rsi(info);
iosf_mbi_unblock_punit_i2c_access();
/* Initialize extcon device */
info->edev = devm_extcon_dev_allocate(&pdev->dev,
axp288_extcon_cables); if (IS_ERR(info->edev)) {
dev_err(&pdev->dev, "failed to allocate memory for extcon\n"); return PTR_ERR(info->edev);
}
/* Register extcon device */
ret = devm_extcon_dev_register(&pdev->dev, info->edev); if (ret) {
dev_err(&pdev->dev, "failed to register extcon device\n"); return ret;
}
for (i = 0; i < EXTCON_IRQ_END; i++) {
pirq = platform_get_irq(pdev, i); if (pirq < 0) return pirq;
info->irq[i] = regmap_irq_get_virq(info->regmap_irqc, pirq); if (info->irq[i] < 0) {
dev_err(&pdev->dev, "failed to get virtual interrupt=%d\n", pirq);
ret = info->irq[i]; return ret;
}
ret = devm_request_threaded_irq(&pdev->dev, info->irq[i],
NULL, axp288_extcon_isr,
IRQF_ONESHOT | IRQF_NO_SUSPEND,
pdev->name, info); if (ret) {
dev_err(&pdev->dev, "failed to request interrupt=%d\n",
info->irq[i]); return ret;
}
}
if (info->id_extcon) {
ret = devm_extcon_register_notifier_all(dev, info->id_extcon,
&info->id_nb); if (ret) return ret;
}
/* Make sure the role-sw is set correctly before doing BC detection */ if (info->role_sw) {
queue_work(system_long_wq, &info->role_work);
flush_work(&info->role_work);
}
/* Start charger cable type detection */
ret = axp288_extcon_enable(info); if (ret < 0) return ret;
/* * Wakeup when a charger is connected to do charger-type * connection and generate an extcon event which makes the * axp288 charger driver set the input current limit.
*/ if (device_may_wakeup(dev))
disable_irq_wake(info->irq[VBUS_RISING_IRQ]);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.