Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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