208 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Driver for Analog Devices (Linear Technology) LT3651 charger IC.4 * Copyright (C) 2017, Topic Embedded Products5 */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 else55 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 type71 * 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 not148 * support IRQs on these pins, userspace will have to poll the sysfs149 * 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");208