cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

lt3651-charger.c (5994B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 *  Driver for Analog Devices (Linear Technology) LT3651 charger IC.
      4 *  Copyright (C) 2017, Topic Embedded Products
      5 */
      6
      7#include <linux/device.h>
      8#include <linux/gpio/consumer.h>
      9#include <linux/init.h>
     10#include <linux/interrupt.h>
     11#include <linux/kernel.h>
     12#include <linux/module.h>
     13#include <linux/platform_device.h>
     14#include <linux/power_supply.h>
     15#include <linux/slab.h>
     16#include <linux/of.h>
     17
     18struct lt3651_charger {
     19	struct power_supply *charger;
     20	struct power_supply_desc charger_desc;
     21	struct gpio_desc *acpr_gpio;
     22	struct gpio_desc *fault_gpio;
     23	struct gpio_desc *chrg_gpio;
     24};
     25
     26static irqreturn_t lt3651_charger_irq(int irq, void *devid)
     27{
     28	struct power_supply *charger = devid;
     29
     30	power_supply_changed(charger);
     31
     32	return IRQ_HANDLED;
     33}
     34
     35static inline struct lt3651_charger *psy_to_lt3651_charger(
     36	struct power_supply *psy)
     37{
     38	return power_supply_get_drvdata(psy);
     39}
     40
     41static int lt3651_charger_get_property(struct power_supply *psy,
     42		enum power_supply_property psp, union power_supply_propval *val)
     43{
     44	struct lt3651_charger *lt3651_charger = psy_to_lt3651_charger(psy);
     45
     46	switch (psp) {
     47	case POWER_SUPPLY_PROP_STATUS:
     48		if (!lt3651_charger->chrg_gpio) {
     49			val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
     50			break;
     51		}
     52		if (gpiod_get_value(lt3651_charger->chrg_gpio))
     53			val->intval = POWER_SUPPLY_STATUS_CHARGING;
     54		else
     55			val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
     56		break;
     57	case POWER_SUPPLY_PROP_ONLINE:
     58		val->intval = gpiod_get_value(lt3651_charger->acpr_gpio);
     59		break;
     60	case POWER_SUPPLY_PROP_HEALTH:
     61		if (!lt3651_charger->fault_gpio) {
     62			val->intval = POWER_SUPPLY_HEALTH_UNKNOWN;
     63			break;
     64		}
     65		if (!gpiod_get_value(lt3651_charger->fault_gpio)) {
     66			val->intval = POWER_SUPPLY_HEALTH_GOOD;
     67			break;
     68		}
     69		/*
     70		 * If the fault pin is active, the chrg pin explains the type
     71		 * of failure.
     72		 */
     73		if (!lt3651_charger->chrg_gpio) {
     74			val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
     75			break;
     76		}
     77		val->intval = gpiod_get_value(lt3651_charger->chrg_gpio) ?
     78				POWER_SUPPLY_HEALTH_OVERHEAT :
     79				POWER_SUPPLY_HEALTH_DEAD;
     80		break;
     81	default:
     82		return -EINVAL;
     83	}
     84
     85	return 0;
     86}
     87
     88static enum power_supply_property lt3651_charger_properties[] = {
     89	POWER_SUPPLY_PROP_STATUS,
     90	POWER_SUPPLY_PROP_ONLINE,
     91	POWER_SUPPLY_PROP_HEALTH,
     92};
     93
     94static int lt3651_charger_probe(struct platform_device *pdev)
     95{
     96	struct power_supply_config psy_cfg = {};
     97	struct lt3651_charger *lt3651_charger;
     98	struct power_supply_desc *charger_desc;
     99	int ret;
    100
    101	lt3651_charger = devm_kzalloc(&pdev->dev, sizeof(*lt3651_charger),
    102					GFP_KERNEL);
    103	if (!lt3651_charger)
    104		return -ENOMEM;
    105
    106	lt3651_charger->acpr_gpio = devm_gpiod_get(&pdev->dev,
    107					"lltc,acpr", GPIOD_IN);
    108	if (IS_ERR(lt3651_charger->acpr_gpio)) {
    109		ret = PTR_ERR(lt3651_charger->acpr_gpio);
    110		dev_err(&pdev->dev, "Failed to acquire acpr GPIO: %d\n", ret);
    111		return ret;
    112	}
    113	lt3651_charger->fault_gpio = devm_gpiod_get_optional(&pdev->dev,
    114					"lltc,fault", GPIOD_IN);
    115	if (IS_ERR(lt3651_charger->fault_gpio)) {
    116		ret = PTR_ERR(lt3651_charger->fault_gpio);
    117		dev_err(&pdev->dev, "Failed to acquire fault GPIO: %d\n", ret);
    118		return ret;
    119	}
    120	lt3651_charger->chrg_gpio = devm_gpiod_get_optional(&pdev->dev,
    121					"lltc,chrg", GPIOD_IN);
    122	if (IS_ERR(lt3651_charger->chrg_gpio)) {
    123		ret = PTR_ERR(lt3651_charger->chrg_gpio);
    124		dev_err(&pdev->dev, "Failed to acquire chrg GPIO: %d\n", ret);
    125		return ret;
    126	}
    127
    128	charger_desc = &lt3651_charger->charger_desc;
    129	charger_desc->name = pdev->dev.of_node->name;
    130	charger_desc->type = POWER_SUPPLY_TYPE_MAINS;
    131	charger_desc->properties = lt3651_charger_properties;
    132	charger_desc->num_properties = ARRAY_SIZE(lt3651_charger_properties);
    133	charger_desc->get_property = lt3651_charger_get_property;
    134	psy_cfg.of_node = pdev->dev.of_node;
    135	psy_cfg.drv_data = lt3651_charger;
    136
    137	lt3651_charger->charger = devm_power_supply_register(&pdev->dev,
    138						      charger_desc, &psy_cfg);
    139	if (IS_ERR(lt3651_charger->charger)) {
    140		ret = PTR_ERR(lt3651_charger->charger);
    141		dev_err(&pdev->dev, "Failed to register power supply: %d\n",
    142			ret);
    143		return ret;
    144	}
    145
    146	/*
    147	 * Acquire IRQs for the GPIO pins if possible. If the system does not
    148	 * support IRQs on these pins, userspace will have to poll the sysfs
    149	 * files manually.
    150	 */
    151	if (lt3651_charger->acpr_gpio) {
    152		ret = gpiod_to_irq(lt3651_charger->acpr_gpio);
    153		if (ret >= 0)
    154			ret = devm_request_any_context_irq(&pdev->dev, ret,
    155				lt3651_charger_irq,
    156				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
    157				dev_name(&pdev->dev), lt3651_charger->charger);
    158		if (ret < 0)
    159			dev_warn(&pdev->dev, "Failed to request acpr irq\n");
    160	}
    161	if (lt3651_charger->fault_gpio) {
    162		ret = gpiod_to_irq(lt3651_charger->fault_gpio);
    163		if (ret >= 0)
    164			ret = devm_request_any_context_irq(&pdev->dev, ret,
    165				lt3651_charger_irq,
    166				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
    167				dev_name(&pdev->dev), lt3651_charger->charger);
    168		if (ret < 0)
    169			dev_warn(&pdev->dev, "Failed to request fault irq\n");
    170	}
    171	if (lt3651_charger->chrg_gpio) {
    172		ret = gpiod_to_irq(lt3651_charger->chrg_gpio);
    173		if (ret >= 0)
    174			ret = devm_request_any_context_irq(&pdev->dev, ret,
    175				lt3651_charger_irq,
    176				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
    177				dev_name(&pdev->dev), lt3651_charger->charger);
    178		if (ret < 0)
    179			dev_warn(&pdev->dev, "Failed to request chrg irq\n");
    180	}
    181
    182	platform_set_drvdata(pdev, lt3651_charger);
    183
    184	return 0;
    185}
    186
    187static const struct of_device_id lt3651_charger_match[] = {
    188	{ .compatible = "lltc,ltc3651-charger" }, /* DEPRECATED */
    189	{ .compatible = "lltc,lt3651-charger" },
    190	{ }
    191};
    192MODULE_DEVICE_TABLE(of, lt3651_charger_match);
    193
    194static struct platform_driver lt3651_charger_driver = {
    195	.probe = lt3651_charger_probe,
    196	.driver = {
    197		.name = "lt3651-charger",
    198		.of_match_table = lt3651_charger_match,
    199	},
    200};
    201
    202module_platform_driver(lt3651_charger_driver);
    203
    204MODULE_AUTHOR("Mike Looijmans <mike.looijmans@topic.nl>");
    205MODULE_DESCRIPTION("Driver for LT3651 charger");
    206MODULE_LICENSE("GPL");
    207MODULE_ALIAS("platform:lt3651-charger");