diff options
author | Hans de Goede <hdegoede@redhat.com> | 2016-12-21 20:28:23 +0300 |
---|---|---|
committer | Sebastian Reichel <sre@kernel.org> | 2017-01-05 00:03:20 +0300 |
commit | 577b1f06e22057e9cdc14b1ee5bd25435c71ff0f (patch) | |
tree | 1a4468f61990f33433b0b4edb2e88b2f86945b31 /drivers/power/supply/axp288_charger.c | |
parent | 7def63ca9cb2ba89a80669dd0bef0e8edfae25d4 (diff) | |
download | linux-577b1f06e22057e9cdc14b1ee5bd25435c71ff0f.tar.xz |
power: supply: axp288_charger: Use one notifier_block per extcon cable
Prior to this commit the code was using 1 notifier_block for all
types of charger cable, this is incorrect as the notifier_block
becomes part of a linked-list and now the same notifier_block
is part of 3 linked lists.
This commit fixes this by using a separate nb per extcon cable.
Note this happened to work fine sofar because axp288_charger was the only
listener, so when added to each of the 3 notifier chains, the next pointer
in the nb would be set to 0, so we've 3 heads pointing to the same nb,
with its next pointing to NULL. But as soon as we mix in a second extcon
consumer things will go boom.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Sebastian Reichel <sre@kernel.org>
Diffstat (limited to 'drivers/power/supply/axp288_charger.c')
-rw-r--r-- | drivers/power/supply/axp288_charger.c | 41 |
1 files changed, 32 insertions, 9 deletions
diff --git a/drivers/power/supply/axp288_charger.c b/drivers/power/supply/axp288_charger.c index ca4fa5124a99..6be2fe27bb07 100644 --- a/drivers/power/supply/axp288_charger.c +++ b/drivers/power/supply/axp288_charger.c @@ -115,6 +115,9 @@ #define AXP288_EXTCON_DEV_NAME "axp288_extcon" #define USB_HOST_EXTCON_DEV_NAME "INT3496:00" +static const unsigned int cable_ids[] = + { EXTCON_CHG_USB_SDP, EXTCON_CHG_USB_CDP, EXTCON_CHG_USB_DCP }; + enum { VBUS_OV_IRQ = 0, CHARGE_DONE_IRQ, @@ -149,7 +152,7 @@ struct axp288_chrg_info { struct extcon_dev *edev; bool connected; enum power_supply_type chg_type; - struct notifier_block nb; + struct notifier_block nb[ARRAY_SIZE(cable_ids)]; struct work_struct work; } cable; @@ -625,14 +628,35 @@ static void axp288_charger_extcon_evt_worker(struct work_struct *work) power_supply_changed(info->psy_usb); } -static int axp288_charger_handle_cable_evt(struct notifier_block *nb, - unsigned long event, void *param) +/* + * We need 3 copies of this, because there is no way to find out for which + * cable id we are being called from the passed in arguments; and we must + * have a separate nb for each extcon_register_notifier call. + */ +static int axp288_charger_handle_cable0_evt(struct notifier_block *nb, + unsigned long event, void *param) { struct axp288_chrg_info *info = - container_of(nb, struct axp288_chrg_info, cable.nb); + container_of(nb, struct axp288_chrg_info, cable.nb[0]); + schedule_work(&info->cable.work); + return NOTIFY_OK; +} +static int axp288_charger_handle_cable1_evt(struct notifier_block *nb, + unsigned long event, void *param) +{ + struct axp288_chrg_info *info = + container_of(nb, struct axp288_chrg_info, cable.nb[1]); schedule_work(&info->cable.work); + return NOTIFY_OK; +} +static int axp288_charger_handle_cable2_evt(struct notifier_block *nb, + unsigned long event, void *param) +{ + struct axp288_chrg_info *info = + container_of(nb, struct axp288_chrg_info, cable.nb[2]); + schedule_work(&info->cable.work); return NOTIFY_OK; } @@ -766,9 +790,6 @@ static int axp288_charger_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); struct power_supply_config charger_cfg = {}; - unsigned int cable_ids[] = { EXTCON_CHG_USB_SDP, EXTCON_CHG_USB_CDP, - EXTCON_CHG_USB_DCP }; - info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); if (!info) return -ENOMEM; @@ -811,10 +832,12 @@ static int axp288_charger_probe(struct platform_device *pdev) /* Register for extcon notification */ INIT_WORK(&info->cable.work, axp288_charger_extcon_evt_worker); - info->cable.nb.notifier_call = axp288_charger_handle_cable_evt; + info->cable.nb[0].notifier_call = axp288_charger_handle_cable0_evt; + info->cable.nb[1].notifier_call = axp288_charger_handle_cable1_evt; + info->cable.nb[2].notifier_call = axp288_charger_handle_cable2_evt; for (i = 0; i < ARRAY_SIZE(cable_ids); i++) { ret = devm_extcon_register_notifier(dev, info->cable.edev, - cable_ids[i], &info->cable.nb); + cable_ids[i], &info->cable.nb[i]); if (ret) { dev_err(dev, "failed to register extcon notifier for %u: %d\n", cable_ids[i], ret); |