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 = <3651_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");