diff options
Diffstat (limited to 'drivers/extcon/extcon-axp288.c')
| -rw-r--r-- | drivers/extcon/extcon-axp288.c | 155 |
1 files changed, 103 insertions, 52 deletions
diff --git a/drivers/extcon/extcon-axp288.c b/drivers/extcon/extcon-axp288.c index a983708b77a6..19856dddade6 100644 --- a/drivers/extcon/extcon-axp288.c +++ b/drivers/extcon/extcon-axp288.c @@ -1,18 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * extcon-axp288.c - X-Power AXP288 PMIC extcon cable detection driver * * Copyright (c) 2017-2018 Hans de Goede <hdegoede@redhat.com> * Copyright (C) 2015 Intel Corporation * Author: Ramakrishna Pallala <ramakrishna.pallala@intel.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <linux/acpi.h> @@ -32,6 +24,7 @@ #include <asm/cpu_device_id.h> #include <asm/intel-family.h> +#include <asm/iosf_mbi.h> /* Power source status register */ #define PS_STAT_VBUS_TRIGGER BIT(0) @@ -115,7 +108,7 @@ struct axp288_extcon_info { }; static const struct x86_cpu_id cherry_trail_cpu_ids[] = { - { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_AIRMONT, X86_FEATURE_ANY }, + X86_MATCH_VFM(INTEL_ATOM_AIRMONT, NULL), {} }; @@ -129,7 +122,6 @@ static const char * const axp288_pwr_up_down_info[] = { "Last shutdown caused by PMIC UVLO threshold", "Last shutdown caused by SOC initiated cold off", "Last shutdown caused by user pressing the power button", - NULL, }; /* @@ -138,18 +130,21 @@ static const char * const axp288_pwr_up_down_info[] = { */ static void axp288_extcon_log_rsi(struct axp288_extcon_info *info) { - const char * const *rsi; unsigned int val, i, clear_mask = 0; + unsigned long bits; int ret; ret = regmap_read(info->regmap, AXP288_PS_BOOT_REASON_REG, &val); - for (i = 0, rsi = axp288_pwr_up_down_info; *rsi; rsi++, i++) { - if (val & BIT(i)) { - dev_dbg(info->dev, "%s\n", *rsi); - clear_mask |= BIT(i); - } + if (ret < 0) { + dev_err(info->dev, "failed to read reset source indicator\n"); + return; } + bits = val & GENMASK(ARRAY_SIZE(axp288_pwr_up_down_info) - 1, 0); + for_each_set_bit(i, &bits, ARRAY_SIZE(axp288_pwr_up_down_info)) + dev_dbg(info->dev, "%s\n", axp288_pwr_up_down_info[i]); + clear_mask = bits; + /* Clear the register value for next reboot (write 1 to clear bit) */ regmap_write(info->regmap, AXP288_PS_BOOT_REASON_REG, clear_mask); } @@ -221,6 +216,10 @@ static int axp288_handle_chrg_det_event(struct axp288_extcon_info *info) unsigned int cable = info->previous_cable; bool vbus_attach = false; + ret = iosf_mbi_block_punit_i2c_access(); + if (ret < 0) + return ret; + vbus_attach = axp288_get_vbus_attach(info); if (!vbus_attach) goto no_vbus; @@ -259,6 +258,8 @@ static int axp288_handle_chrg_det_event(struct axp288_extcon_info *info) } 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); @@ -281,6 +282,8 @@ no_vbus: return 0; dev_det_ret: + iosf_mbi_unblock_punit_i2c_access(); + if (ret < 0) dev_err(info->dev, "failed to detect BC Mod\n"); @@ -311,13 +314,23 @@ static irqreturn_t axp288_extcon_isr(int irq, void *data) return IRQ_HANDLED; } -static void axp288_extcon_enable(struct axp288_extcon_info *info) +static int axp288_extcon_enable(struct axp288_extcon_info *info) { + int ret = 0; + + ret = iosf_mbi_block_punit_i2c_access(); + if (ret < 0) + return ret; + regmap_update_bits(info->regmap, AXP288_BC_GLOBAL_REG, BC_GLOBAL_RUN, 0); /* Enable the charger detection logic */ regmap_update_bits(info->regmap, AXP288_BC_GLOBAL_REG, BC_GLOBAL_RUN, BC_GLOBAL_RUN); + + iosf_mbi_unblock_punit_i2c_access(); + + return ret; } static void axp288_put_role_sw(void *data) @@ -328,12 +341,31 @@ static void axp288_put_role_sw(void *data) usb_role_switch_put(info->role_sw); } +static int axp288_extcon_find_role_sw(struct axp288_extcon_info *info) +{ + const struct software_node *swnode; + struct fwnode_handle *fwnode; + + if (!x86_match_cpu(cherry_trail_cpu_ids)) + return 0; + + swnode = software_node_find_by_name(NULL, "intel-xhci-usb-sw"); + if (!swnode) + return -EPROBE_DEFER; + + fwnode = software_node_fwnode(swnode); + info->role_sw = usb_role_switch_find_by_fwnode(fwnode); + fwnode_handle_put(fwnode); + + return info->role_sw ? 0 : -EPROBE_DEFER; +} + static int axp288_extcon_probe(struct platform_device *pdev) { struct axp288_extcon_info *info; struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); struct device *dev = &pdev->dev; - const char *name; + struct acpi_device *adev; int ret, i, pirq; info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); @@ -349,19 +381,21 @@ static int axp288_extcon_probe(struct platform_device *pdev) platform_set_drvdata(pdev, info); - info->role_sw = usb_role_switch_get(dev); - if (IS_ERR(info->role_sw)) - return PTR_ERR(info->role_sw); + 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; - name = acpi_dev_get_first_match_name("INT3496", NULL, -1); - if (name) { - info->id_extcon = extcon_get_extcon_dev(name); - if (!info->id_extcon) - return -EPROBE_DEFER; + 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 { @@ -369,10 +403,16 @@ static int axp288_extcon_probe(struct platform_device *pdev) } } + 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); @@ -426,11 +466,44 @@ static int axp288_extcon_probe(struct platform_device *pdev) } /* Start charger cable type detection */ - axp288_extcon_enable(info); + ret = axp288_extcon_enable(info); + if (ret < 0) + return ret; + + devm_device_init_wakeup(dev); + platform_set_drvdata(pdev, info); return 0; } +static int __maybe_unused axp288_extcon_suspend(struct device *dev) +{ + struct axp288_extcon_info *info = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + enable_irq_wake(info->irq[VBUS_RISING_IRQ]); + + return 0; +} + +static int __maybe_unused axp288_extcon_resume(struct device *dev) +{ + struct axp288_extcon_info *info = dev_get_drvdata(dev); + + /* + * 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]); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(axp288_extcon_pm_ops, axp288_extcon_suspend, + axp288_extcon_resume); + static const struct platform_device_id axp288_extcon_table[] = { { .name = "axp288_extcon" }, {}, @@ -442,32 +515,10 @@ static struct platform_driver axp288_extcon_driver = { .id_table = axp288_extcon_table, .driver = { .name = "axp288_extcon", + .pm = &axp288_extcon_pm_ops, }, }; - -static struct device_connection axp288_extcon_role_sw_conn = { - .endpoint[0] = "axp288_extcon", - .endpoint[1] = "intel_xhci_usb_sw-role-switch", - .id = "usb-role-switch", -}; - -static int __init axp288_extcon_init(void) -{ - if (x86_match_cpu(cherry_trail_cpu_ids)) - device_connection_add(&axp288_extcon_role_sw_conn); - - return platform_driver_register(&axp288_extcon_driver); -} -module_init(axp288_extcon_init); - -static void __exit axp288_extcon_exit(void) -{ - if (x86_match_cpu(cherry_trail_cpu_ids)) - device_connection_remove(&axp288_extcon_role_sw_conn); - - platform_driver_unregister(&axp288_extcon_driver); -} -module_exit(axp288_extcon_exit); +module_platform_driver(axp288_extcon_driver); MODULE_AUTHOR("Ramakrishna Pallala <ramakrishna.pallala@intel.com>"); MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); |
