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