18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Common power driver for PDAs and phones with one or two external 48c2ecf20Sopenharmony_ci * power supplies (AC/USB) connected to main and backup batteries, 58c2ecf20Sopenharmony_ci * and optional builtin charger. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright © 2007 Anton Vorontsov <cbou@mail.ru> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 128c2ecf20Sopenharmony_ci#include <linux/err.h> 138c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 148c2ecf20Sopenharmony_ci#include <linux/notifier.h> 158c2ecf20Sopenharmony_ci#include <linux/power_supply.h> 168c2ecf20Sopenharmony_ci#include <linux/pda_power.h> 178c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 188c2ecf20Sopenharmony_ci#include <linux/timer.h> 198c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 208c2ecf20Sopenharmony_ci#include <linux/usb/otg.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistatic inline unsigned int get_irq_flags(struct resource *res) 238c2ecf20Sopenharmony_ci{ 248c2ecf20Sopenharmony_ci return IRQF_SHARED | (res->flags & IRQF_TRIGGER_MASK); 258c2ecf20Sopenharmony_ci} 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic struct device *dev; 288c2ecf20Sopenharmony_cistatic struct pda_power_pdata *pdata; 298c2ecf20Sopenharmony_cistatic struct resource *ac_irq, *usb_irq; 308c2ecf20Sopenharmony_cistatic struct delayed_work charger_work; 318c2ecf20Sopenharmony_cistatic struct delayed_work polling_work; 328c2ecf20Sopenharmony_cistatic struct delayed_work supply_work; 338c2ecf20Sopenharmony_cistatic int polling; 348c2ecf20Sopenharmony_cistatic struct power_supply *pda_psy_ac, *pda_psy_usb; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_USB_PHY) 378c2ecf20Sopenharmony_cistatic struct usb_phy *transceiver; 388c2ecf20Sopenharmony_cistatic struct notifier_block otg_nb; 398c2ecf20Sopenharmony_ci#endif 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic struct regulator *ac_draw; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cienum { 448c2ecf20Sopenharmony_ci PDA_PSY_OFFLINE = 0, 458c2ecf20Sopenharmony_ci PDA_PSY_ONLINE = 1, 468c2ecf20Sopenharmony_ci PDA_PSY_TO_CHANGE, 478c2ecf20Sopenharmony_ci}; 488c2ecf20Sopenharmony_cistatic int new_ac_status = -1; 498c2ecf20Sopenharmony_cistatic int new_usb_status = -1; 508c2ecf20Sopenharmony_cistatic int ac_status = -1; 518c2ecf20Sopenharmony_cistatic int usb_status = -1; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic int pda_power_get_property(struct power_supply *psy, 548c2ecf20Sopenharmony_ci enum power_supply_property psp, 558c2ecf20Sopenharmony_ci union power_supply_propval *val) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci switch (psp) { 588c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_ONLINE: 598c2ecf20Sopenharmony_ci if (psy->desc->type == POWER_SUPPLY_TYPE_MAINS) 608c2ecf20Sopenharmony_ci val->intval = pdata->is_ac_online ? 618c2ecf20Sopenharmony_ci pdata->is_ac_online() : 0; 628c2ecf20Sopenharmony_ci else 638c2ecf20Sopenharmony_ci val->intval = pdata->is_usb_online ? 648c2ecf20Sopenharmony_ci pdata->is_usb_online() : 0; 658c2ecf20Sopenharmony_ci break; 668c2ecf20Sopenharmony_ci default: 678c2ecf20Sopenharmony_ci return -EINVAL; 688c2ecf20Sopenharmony_ci } 698c2ecf20Sopenharmony_ci return 0; 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic enum power_supply_property pda_power_props[] = { 738c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_ONLINE, 748c2ecf20Sopenharmony_ci}; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic char *pda_power_supplied_to[] = { 778c2ecf20Sopenharmony_ci "main-battery", 788c2ecf20Sopenharmony_ci "backup-battery", 798c2ecf20Sopenharmony_ci}; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic const struct power_supply_desc pda_psy_ac_desc = { 828c2ecf20Sopenharmony_ci .name = "ac", 838c2ecf20Sopenharmony_ci .type = POWER_SUPPLY_TYPE_MAINS, 848c2ecf20Sopenharmony_ci .properties = pda_power_props, 858c2ecf20Sopenharmony_ci .num_properties = ARRAY_SIZE(pda_power_props), 868c2ecf20Sopenharmony_ci .get_property = pda_power_get_property, 878c2ecf20Sopenharmony_ci}; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic const struct power_supply_desc pda_psy_usb_desc = { 908c2ecf20Sopenharmony_ci .name = "usb", 918c2ecf20Sopenharmony_ci .type = POWER_SUPPLY_TYPE_USB, 928c2ecf20Sopenharmony_ci .properties = pda_power_props, 938c2ecf20Sopenharmony_ci .num_properties = ARRAY_SIZE(pda_power_props), 948c2ecf20Sopenharmony_ci .get_property = pda_power_get_property, 958c2ecf20Sopenharmony_ci}; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic void update_status(void) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci if (pdata->is_ac_online) 1008c2ecf20Sopenharmony_ci new_ac_status = !!pdata->is_ac_online(); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci if (pdata->is_usb_online) 1038c2ecf20Sopenharmony_ci new_usb_status = !!pdata->is_usb_online(); 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic void update_charger(void) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci static int regulator_enabled; 1098c2ecf20Sopenharmony_ci int max_uA = pdata->ac_max_uA; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci if (pdata->set_charge) { 1128c2ecf20Sopenharmony_ci if (new_ac_status > 0) { 1138c2ecf20Sopenharmony_ci dev_dbg(dev, "charger on (AC)\n"); 1148c2ecf20Sopenharmony_ci pdata->set_charge(PDA_POWER_CHARGE_AC); 1158c2ecf20Sopenharmony_ci } else if (new_usb_status > 0) { 1168c2ecf20Sopenharmony_ci dev_dbg(dev, "charger on (USB)\n"); 1178c2ecf20Sopenharmony_ci pdata->set_charge(PDA_POWER_CHARGE_USB); 1188c2ecf20Sopenharmony_ci } else { 1198c2ecf20Sopenharmony_ci dev_dbg(dev, "charger off\n"); 1208c2ecf20Sopenharmony_ci pdata->set_charge(0); 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci } else if (ac_draw) { 1238c2ecf20Sopenharmony_ci if (new_ac_status > 0) { 1248c2ecf20Sopenharmony_ci regulator_set_current_limit(ac_draw, max_uA, max_uA); 1258c2ecf20Sopenharmony_ci if (!regulator_enabled) { 1268c2ecf20Sopenharmony_ci dev_dbg(dev, "charger on (AC)\n"); 1278c2ecf20Sopenharmony_ci WARN_ON(regulator_enable(ac_draw)); 1288c2ecf20Sopenharmony_ci regulator_enabled = 1; 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci } else { 1318c2ecf20Sopenharmony_ci if (regulator_enabled) { 1328c2ecf20Sopenharmony_ci dev_dbg(dev, "charger off\n"); 1338c2ecf20Sopenharmony_ci WARN_ON(regulator_disable(ac_draw)); 1348c2ecf20Sopenharmony_ci regulator_enabled = 0; 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic void supply_work_func(struct work_struct *work) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci if (ac_status == PDA_PSY_TO_CHANGE) { 1438c2ecf20Sopenharmony_ci ac_status = new_ac_status; 1448c2ecf20Sopenharmony_ci power_supply_changed(pda_psy_ac); 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci if (usb_status == PDA_PSY_TO_CHANGE) { 1488c2ecf20Sopenharmony_ci usb_status = new_usb_status; 1498c2ecf20Sopenharmony_ci power_supply_changed(pda_psy_usb); 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic void psy_changed(void) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci update_charger(); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci /* 1588c2ecf20Sopenharmony_ci * Okay, charger set. Now wait a bit before notifying supplicants, 1598c2ecf20Sopenharmony_ci * charge power should stabilize. 1608c2ecf20Sopenharmony_ci */ 1618c2ecf20Sopenharmony_ci cancel_delayed_work(&supply_work); 1628c2ecf20Sopenharmony_ci schedule_delayed_work(&supply_work, 1638c2ecf20Sopenharmony_ci msecs_to_jiffies(pdata->wait_for_charger)); 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic void charger_work_func(struct work_struct *work) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci update_status(); 1698c2ecf20Sopenharmony_ci psy_changed(); 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic irqreturn_t power_changed_isr(int irq, void *power_supply) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci if (power_supply == pda_psy_ac) 1758c2ecf20Sopenharmony_ci ac_status = PDA_PSY_TO_CHANGE; 1768c2ecf20Sopenharmony_ci else if (power_supply == pda_psy_usb) 1778c2ecf20Sopenharmony_ci usb_status = PDA_PSY_TO_CHANGE; 1788c2ecf20Sopenharmony_ci else 1798c2ecf20Sopenharmony_ci return IRQ_NONE; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci /* 1828c2ecf20Sopenharmony_ci * Wait a bit before reading ac/usb line status and setting charger, 1838c2ecf20Sopenharmony_ci * because ac/usb status readings may lag from irq. 1848c2ecf20Sopenharmony_ci */ 1858c2ecf20Sopenharmony_ci cancel_delayed_work(&charger_work); 1868c2ecf20Sopenharmony_ci schedule_delayed_work(&charger_work, 1878c2ecf20Sopenharmony_ci msecs_to_jiffies(pdata->wait_for_status)); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic void polling_work_func(struct work_struct *work) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci int changed = 0; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci dev_dbg(dev, "polling...\n"); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci update_status(); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci if (!ac_irq && new_ac_status != ac_status) { 2018c2ecf20Sopenharmony_ci ac_status = PDA_PSY_TO_CHANGE; 2028c2ecf20Sopenharmony_ci changed = 1; 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci if (!usb_irq && new_usb_status != usb_status) { 2068c2ecf20Sopenharmony_ci usb_status = PDA_PSY_TO_CHANGE; 2078c2ecf20Sopenharmony_ci changed = 1; 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci if (changed) 2118c2ecf20Sopenharmony_ci psy_changed(); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci cancel_delayed_work(&polling_work); 2148c2ecf20Sopenharmony_ci schedule_delayed_work(&polling_work, 2158c2ecf20Sopenharmony_ci msecs_to_jiffies(pdata->polling_interval)); 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_USB_PHY) 2198c2ecf20Sopenharmony_cistatic int otg_is_usb_online(void) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci return (transceiver->last_event == USB_EVENT_VBUS || 2228c2ecf20Sopenharmony_ci transceiver->last_event == USB_EVENT_ENUMERATED); 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cistatic int otg_is_ac_online(void) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci return (transceiver->last_event == USB_EVENT_CHARGER); 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cistatic int otg_handle_notification(struct notifier_block *nb, 2318c2ecf20Sopenharmony_ci unsigned long event, void *unused) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci switch (event) { 2348c2ecf20Sopenharmony_ci case USB_EVENT_CHARGER: 2358c2ecf20Sopenharmony_ci ac_status = PDA_PSY_TO_CHANGE; 2368c2ecf20Sopenharmony_ci break; 2378c2ecf20Sopenharmony_ci case USB_EVENT_VBUS: 2388c2ecf20Sopenharmony_ci case USB_EVENT_ENUMERATED: 2398c2ecf20Sopenharmony_ci usb_status = PDA_PSY_TO_CHANGE; 2408c2ecf20Sopenharmony_ci break; 2418c2ecf20Sopenharmony_ci case USB_EVENT_NONE: 2428c2ecf20Sopenharmony_ci ac_status = PDA_PSY_TO_CHANGE; 2438c2ecf20Sopenharmony_ci usb_status = PDA_PSY_TO_CHANGE; 2448c2ecf20Sopenharmony_ci break; 2458c2ecf20Sopenharmony_ci default: 2468c2ecf20Sopenharmony_ci return NOTIFY_OK; 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci /* 2508c2ecf20Sopenharmony_ci * Wait a bit before reading ac/usb line status and setting charger, 2518c2ecf20Sopenharmony_ci * because ac/usb status readings may lag from irq. 2528c2ecf20Sopenharmony_ci */ 2538c2ecf20Sopenharmony_ci cancel_delayed_work(&charger_work); 2548c2ecf20Sopenharmony_ci schedule_delayed_work(&charger_work, 2558c2ecf20Sopenharmony_ci msecs_to_jiffies(pdata->wait_for_status)); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci return NOTIFY_OK; 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ci#endif 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_cistatic int pda_power_probe(struct platform_device *pdev) 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci struct power_supply_config psy_cfg = {}; 2648c2ecf20Sopenharmony_ci int ret = 0; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci dev = &pdev->dev; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci if (pdev->id != -1) { 2698c2ecf20Sopenharmony_ci dev_err(dev, "it's meaningless to register several " 2708c2ecf20Sopenharmony_ci "pda_powers; use id = -1\n"); 2718c2ecf20Sopenharmony_ci ret = -EINVAL; 2728c2ecf20Sopenharmony_ci goto wrongid; 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci pdata = pdev->dev.platform_data; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci if (pdata->init) { 2788c2ecf20Sopenharmony_ci ret = pdata->init(dev); 2798c2ecf20Sopenharmony_ci if (ret < 0) 2808c2ecf20Sopenharmony_ci goto init_failed; 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci ac_draw = regulator_get(dev, "ac_draw"); 2848c2ecf20Sopenharmony_ci if (IS_ERR(ac_draw)) { 2858c2ecf20Sopenharmony_ci dev_dbg(dev, "couldn't get ac_draw regulator\n"); 2868c2ecf20Sopenharmony_ci ac_draw = NULL; 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci update_status(); 2908c2ecf20Sopenharmony_ci update_charger(); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci if (!pdata->wait_for_status) 2938c2ecf20Sopenharmony_ci pdata->wait_for_status = 500; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci if (!pdata->wait_for_charger) 2968c2ecf20Sopenharmony_ci pdata->wait_for_charger = 500; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci if (!pdata->polling_interval) 2998c2ecf20Sopenharmony_ci pdata->polling_interval = 2000; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci if (!pdata->ac_max_uA) 3028c2ecf20Sopenharmony_ci pdata->ac_max_uA = 500000; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&charger_work, charger_work_func); 3058c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&supply_work, supply_work_func); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci ac_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "ac"); 3088c2ecf20Sopenharmony_ci usb_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "usb"); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci if (pdata->supplied_to) { 3118c2ecf20Sopenharmony_ci psy_cfg.supplied_to = pdata->supplied_to; 3128c2ecf20Sopenharmony_ci psy_cfg.num_supplicants = pdata->num_supplicants; 3138c2ecf20Sopenharmony_ci } else { 3148c2ecf20Sopenharmony_ci psy_cfg.supplied_to = pda_power_supplied_to; 3158c2ecf20Sopenharmony_ci psy_cfg.num_supplicants = ARRAY_SIZE(pda_power_supplied_to); 3168c2ecf20Sopenharmony_ci } 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_USB_PHY) 3198c2ecf20Sopenharmony_ci transceiver = usb_get_phy(USB_PHY_TYPE_USB2); 3208c2ecf20Sopenharmony_ci if (!IS_ERR_OR_NULL(transceiver)) { 3218c2ecf20Sopenharmony_ci if (!pdata->is_usb_online) 3228c2ecf20Sopenharmony_ci pdata->is_usb_online = otg_is_usb_online; 3238c2ecf20Sopenharmony_ci if (!pdata->is_ac_online) 3248c2ecf20Sopenharmony_ci pdata->is_ac_online = otg_is_ac_online; 3258c2ecf20Sopenharmony_ci } 3268c2ecf20Sopenharmony_ci#endif 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci if (pdata->is_ac_online) { 3298c2ecf20Sopenharmony_ci pda_psy_ac = power_supply_register(&pdev->dev, 3308c2ecf20Sopenharmony_ci &pda_psy_ac_desc, &psy_cfg); 3318c2ecf20Sopenharmony_ci if (IS_ERR(pda_psy_ac)) { 3328c2ecf20Sopenharmony_ci dev_err(dev, "failed to register %s power supply\n", 3338c2ecf20Sopenharmony_ci pda_psy_ac_desc.name); 3348c2ecf20Sopenharmony_ci ret = PTR_ERR(pda_psy_ac); 3358c2ecf20Sopenharmony_ci goto ac_supply_failed; 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci if (ac_irq) { 3398c2ecf20Sopenharmony_ci ret = request_irq(ac_irq->start, power_changed_isr, 3408c2ecf20Sopenharmony_ci get_irq_flags(ac_irq), ac_irq->name, 3418c2ecf20Sopenharmony_ci pda_psy_ac); 3428c2ecf20Sopenharmony_ci if (ret) { 3438c2ecf20Sopenharmony_ci dev_err(dev, "request ac irq failed\n"); 3448c2ecf20Sopenharmony_ci goto ac_irq_failed; 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci } else { 3478c2ecf20Sopenharmony_ci polling = 1; 3488c2ecf20Sopenharmony_ci } 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci if (pdata->is_usb_online) { 3528c2ecf20Sopenharmony_ci pda_psy_usb = power_supply_register(&pdev->dev, 3538c2ecf20Sopenharmony_ci &pda_psy_usb_desc, 3548c2ecf20Sopenharmony_ci &psy_cfg); 3558c2ecf20Sopenharmony_ci if (IS_ERR(pda_psy_usb)) { 3568c2ecf20Sopenharmony_ci dev_err(dev, "failed to register %s power supply\n", 3578c2ecf20Sopenharmony_ci pda_psy_usb_desc.name); 3588c2ecf20Sopenharmony_ci ret = PTR_ERR(pda_psy_usb); 3598c2ecf20Sopenharmony_ci goto usb_supply_failed; 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci if (usb_irq) { 3638c2ecf20Sopenharmony_ci ret = request_irq(usb_irq->start, power_changed_isr, 3648c2ecf20Sopenharmony_ci get_irq_flags(usb_irq), 3658c2ecf20Sopenharmony_ci usb_irq->name, pda_psy_usb); 3668c2ecf20Sopenharmony_ci if (ret) { 3678c2ecf20Sopenharmony_ci dev_err(dev, "request usb irq failed\n"); 3688c2ecf20Sopenharmony_ci goto usb_irq_failed; 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci } else { 3718c2ecf20Sopenharmony_ci polling = 1; 3728c2ecf20Sopenharmony_ci } 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_USB_PHY) 3768c2ecf20Sopenharmony_ci if (!IS_ERR_OR_NULL(transceiver) && pdata->use_otg_notifier) { 3778c2ecf20Sopenharmony_ci otg_nb.notifier_call = otg_handle_notification; 3788c2ecf20Sopenharmony_ci ret = usb_register_notifier(transceiver, &otg_nb); 3798c2ecf20Sopenharmony_ci if (ret) { 3808c2ecf20Sopenharmony_ci dev_err(dev, "failure to register otg notifier\n"); 3818c2ecf20Sopenharmony_ci goto otg_reg_notifier_failed; 3828c2ecf20Sopenharmony_ci } 3838c2ecf20Sopenharmony_ci polling = 0; 3848c2ecf20Sopenharmony_ci } 3858c2ecf20Sopenharmony_ci#endif 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci if (polling) { 3888c2ecf20Sopenharmony_ci dev_dbg(dev, "will poll for status\n"); 3898c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&polling_work, polling_work_func); 3908c2ecf20Sopenharmony_ci cancel_delayed_work(&polling_work); 3918c2ecf20Sopenharmony_ci schedule_delayed_work(&polling_work, 3928c2ecf20Sopenharmony_ci msecs_to_jiffies(pdata->polling_interval)); 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci if (ac_irq || usb_irq) 3968c2ecf20Sopenharmony_ci device_init_wakeup(&pdev->dev, 1); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci return 0; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_USB_PHY) 4018c2ecf20Sopenharmony_ciotg_reg_notifier_failed: 4028c2ecf20Sopenharmony_ci if (pdata->is_usb_online && usb_irq) 4038c2ecf20Sopenharmony_ci free_irq(usb_irq->start, pda_psy_usb); 4048c2ecf20Sopenharmony_ci#endif 4058c2ecf20Sopenharmony_ciusb_irq_failed: 4068c2ecf20Sopenharmony_ci if (pdata->is_usb_online) 4078c2ecf20Sopenharmony_ci power_supply_unregister(pda_psy_usb); 4088c2ecf20Sopenharmony_ciusb_supply_failed: 4098c2ecf20Sopenharmony_ci if (pdata->is_ac_online && ac_irq) 4108c2ecf20Sopenharmony_ci free_irq(ac_irq->start, pda_psy_ac); 4118c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_USB_PHY) 4128c2ecf20Sopenharmony_ci if (!IS_ERR_OR_NULL(transceiver)) 4138c2ecf20Sopenharmony_ci usb_put_phy(transceiver); 4148c2ecf20Sopenharmony_ci#endif 4158c2ecf20Sopenharmony_ciac_irq_failed: 4168c2ecf20Sopenharmony_ci if (pdata->is_ac_online) 4178c2ecf20Sopenharmony_ci power_supply_unregister(pda_psy_ac); 4188c2ecf20Sopenharmony_ciac_supply_failed: 4198c2ecf20Sopenharmony_ci if (ac_draw) { 4208c2ecf20Sopenharmony_ci regulator_put(ac_draw); 4218c2ecf20Sopenharmony_ci ac_draw = NULL; 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_ci if (pdata->exit) 4248c2ecf20Sopenharmony_ci pdata->exit(dev); 4258c2ecf20Sopenharmony_ciinit_failed: 4268c2ecf20Sopenharmony_ciwrongid: 4278c2ecf20Sopenharmony_ci return ret; 4288c2ecf20Sopenharmony_ci} 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_cistatic int pda_power_remove(struct platform_device *pdev) 4318c2ecf20Sopenharmony_ci{ 4328c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_USB_PHY) 4338c2ecf20Sopenharmony_ci if (!IS_ERR_OR_NULL(transceiver) && pdata->use_otg_notifier) 4348c2ecf20Sopenharmony_ci usb_unregister_notifier(transceiver, &otg_nb); 4358c2ecf20Sopenharmony_ci#endif 4368c2ecf20Sopenharmony_ci if (pdata->is_usb_online && usb_irq) 4378c2ecf20Sopenharmony_ci free_irq(usb_irq->start, pda_psy_usb); 4388c2ecf20Sopenharmony_ci if (pdata->is_ac_online && ac_irq) 4398c2ecf20Sopenharmony_ci free_irq(ac_irq->start, pda_psy_ac); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci if (polling) 4428c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&polling_work); 4438c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&charger_work); 4448c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&supply_work); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci if (pdata->is_usb_online) 4478c2ecf20Sopenharmony_ci power_supply_unregister(pda_psy_usb); 4488c2ecf20Sopenharmony_ci if (pdata->is_ac_online) 4498c2ecf20Sopenharmony_ci power_supply_unregister(pda_psy_ac); 4508c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_USB_PHY) 4518c2ecf20Sopenharmony_ci if (!IS_ERR_OR_NULL(transceiver)) 4528c2ecf20Sopenharmony_ci usb_put_phy(transceiver); 4538c2ecf20Sopenharmony_ci#endif 4548c2ecf20Sopenharmony_ci if (ac_draw) { 4558c2ecf20Sopenharmony_ci regulator_put(ac_draw); 4568c2ecf20Sopenharmony_ci ac_draw = NULL; 4578c2ecf20Sopenharmony_ci } 4588c2ecf20Sopenharmony_ci if (pdata->exit) 4598c2ecf20Sopenharmony_ci pdata->exit(dev); 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci return 0; 4628c2ecf20Sopenharmony_ci} 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 4658c2ecf20Sopenharmony_cistatic int ac_wakeup_enabled; 4668c2ecf20Sopenharmony_cistatic int usb_wakeup_enabled; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_cistatic int pda_power_suspend(struct platform_device *pdev, pm_message_t state) 4698c2ecf20Sopenharmony_ci{ 4708c2ecf20Sopenharmony_ci if (pdata->suspend) { 4718c2ecf20Sopenharmony_ci int ret = pdata->suspend(state); 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci if (ret) 4748c2ecf20Sopenharmony_ci return ret; 4758c2ecf20Sopenharmony_ci } 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci if (device_may_wakeup(&pdev->dev)) { 4788c2ecf20Sopenharmony_ci if (ac_irq) 4798c2ecf20Sopenharmony_ci ac_wakeup_enabled = !enable_irq_wake(ac_irq->start); 4808c2ecf20Sopenharmony_ci if (usb_irq) 4818c2ecf20Sopenharmony_ci usb_wakeup_enabled = !enable_irq_wake(usb_irq->start); 4828c2ecf20Sopenharmony_ci } 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci return 0; 4858c2ecf20Sopenharmony_ci} 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_cistatic int pda_power_resume(struct platform_device *pdev) 4888c2ecf20Sopenharmony_ci{ 4898c2ecf20Sopenharmony_ci if (device_may_wakeup(&pdev->dev)) { 4908c2ecf20Sopenharmony_ci if (usb_irq && usb_wakeup_enabled) 4918c2ecf20Sopenharmony_ci disable_irq_wake(usb_irq->start); 4928c2ecf20Sopenharmony_ci if (ac_irq && ac_wakeup_enabled) 4938c2ecf20Sopenharmony_ci disable_irq_wake(ac_irq->start); 4948c2ecf20Sopenharmony_ci } 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci if (pdata->resume) 4978c2ecf20Sopenharmony_ci return pdata->resume(); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci return 0; 5008c2ecf20Sopenharmony_ci} 5018c2ecf20Sopenharmony_ci#else 5028c2ecf20Sopenharmony_ci#define pda_power_suspend NULL 5038c2ecf20Sopenharmony_ci#define pda_power_resume NULL 5048c2ecf20Sopenharmony_ci#endif /* CONFIG_PM */ 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_cistatic struct platform_driver pda_power_pdrv = { 5078c2ecf20Sopenharmony_ci .driver = { 5088c2ecf20Sopenharmony_ci .name = "pda-power", 5098c2ecf20Sopenharmony_ci }, 5108c2ecf20Sopenharmony_ci .probe = pda_power_probe, 5118c2ecf20Sopenharmony_ci .remove = pda_power_remove, 5128c2ecf20Sopenharmony_ci .suspend = pda_power_suspend, 5138c2ecf20Sopenharmony_ci .resume = pda_power_resume, 5148c2ecf20Sopenharmony_ci}; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_cimodule_platform_driver(pda_power_pdrv); 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 5198c2ecf20Sopenharmony_ciMODULE_AUTHOR("Anton Vorontsov <cbou@mail.ru>"); 5208c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:pda-power"); 521