0001
0002
0003
0004
0005
0006
0007 #include <linux/device.h>
0008 #include <linux/gpio/consumer.h>
0009 #include <linux/init.h>
0010 #include <linux/interrupt.h>
0011 #include <linux/kernel.h>
0012 #include <linux/module.h>
0013 #include <linux/platform_device.h>
0014 #include <linux/power_supply.h>
0015 #include <linux/slab.h>
0016 #include <linux/of.h>
0017
0018 struct lt3651_charger {
0019 struct power_supply *charger;
0020 struct power_supply_desc charger_desc;
0021 struct gpio_desc *acpr_gpio;
0022 struct gpio_desc *fault_gpio;
0023 struct gpio_desc *chrg_gpio;
0024 };
0025
0026 static irqreturn_t lt3651_charger_irq(int irq, void *devid)
0027 {
0028 struct power_supply *charger = devid;
0029
0030 power_supply_changed(charger);
0031
0032 return IRQ_HANDLED;
0033 }
0034
0035 static inline struct lt3651_charger *psy_to_lt3651_charger(
0036 struct power_supply *psy)
0037 {
0038 return power_supply_get_drvdata(psy);
0039 }
0040
0041 static int lt3651_charger_get_property(struct power_supply *psy,
0042 enum power_supply_property psp, union power_supply_propval *val)
0043 {
0044 struct lt3651_charger *lt3651_charger = psy_to_lt3651_charger(psy);
0045
0046 switch (psp) {
0047 case POWER_SUPPLY_PROP_STATUS:
0048 if (!lt3651_charger->chrg_gpio) {
0049 val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
0050 break;
0051 }
0052 if (gpiod_get_value(lt3651_charger->chrg_gpio))
0053 val->intval = POWER_SUPPLY_STATUS_CHARGING;
0054 else
0055 val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
0056 break;
0057 case POWER_SUPPLY_PROP_ONLINE:
0058 val->intval = gpiod_get_value(lt3651_charger->acpr_gpio);
0059 break;
0060 case POWER_SUPPLY_PROP_HEALTH:
0061 if (!lt3651_charger->fault_gpio) {
0062 val->intval = POWER_SUPPLY_HEALTH_UNKNOWN;
0063 break;
0064 }
0065 if (!gpiod_get_value(lt3651_charger->fault_gpio)) {
0066 val->intval = POWER_SUPPLY_HEALTH_GOOD;
0067 break;
0068 }
0069
0070
0071
0072
0073 if (!lt3651_charger->chrg_gpio) {
0074 val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
0075 break;
0076 }
0077 val->intval = gpiod_get_value(lt3651_charger->chrg_gpio) ?
0078 POWER_SUPPLY_HEALTH_OVERHEAT :
0079 POWER_SUPPLY_HEALTH_DEAD;
0080 break;
0081 default:
0082 return -EINVAL;
0083 }
0084
0085 return 0;
0086 }
0087
0088 static enum power_supply_property lt3651_charger_properties[] = {
0089 POWER_SUPPLY_PROP_STATUS,
0090 POWER_SUPPLY_PROP_ONLINE,
0091 POWER_SUPPLY_PROP_HEALTH,
0092 };
0093
0094 static int lt3651_charger_probe(struct platform_device *pdev)
0095 {
0096 struct power_supply_config psy_cfg = {};
0097 struct lt3651_charger *lt3651_charger;
0098 struct power_supply_desc *charger_desc;
0099 int ret;
0100
0101 lt3651_charger = devm_kzalloc(&pdev->dev, sizeof(*lt3651_charger),
0102 GFP_KERNEL);
0103 if (!lt3651_charger)
0104 return -ENOMEM;
0105
0106 lt3651_charger->acpr_gpio = devm_gpiod_get(&pdev->dev,
0107 "lltc,acpr", GPIOD_IN);
0108 if (IS_ERR(lt3651_charger->acpr_gpio)) {
0109 ret = PTR_ERR(lt3651_charger->acpr_gpio);
0110 dev_err(&pdev->dev, "Failed to acquire acpr GPIO: %d\n", ret);
0111 return ret;
0112 }
0113 lt3651_charger->fault_gpio = devm_gpiod_get_optional(&pdev->dev,
0114 "lltc,fault", GPIOD_IN);
0115 if (IS_ERR(lt3651_charger->fault_gpio)) {
0116 ret = PTR_ERR(lt3651_charger->fault_gpio);
0117 dev_err(&pdev->dev, "Failed to acquire fault GPIO: %d\n", ret);
0118 return ret;
0119 }
0120 lt3651_charger->chrg_gpio = devm_gpiod_get_optional(&pdev->dev,
0121 "lltc,chrg", GPIOD_IN);
0122 if (IS_ERR(lt3651_charger->chrg_gpio)) {
0123 ret = PTR_ERR(lt3651_charger->chrg_gpio);
0124 dev_err(&pdev->dev, "Failed to acquire chrg GPIO: %d\n", ret);
0125 return ret;
0126 }
0127
0128 charger_desc = <3651_charger->charger_desc;
0129 charger_desc->name = pdev->dev.of_node->name;
0130 charger_desc->type = POWER_SUPPLY_TYPE_MAINS;
0131 charger_desc->properties = lt3651_charger_properties;
0132 charger_desc->num_properties = ARRAY_SIZE(lt3651_charger_properties);
0133 charger_desc->get_property = lt3651_charger_get_property;
0134 psy_cfg.of_node = pdev->dev.of_node;
0135 psy_cfg.drv_data = lt3651_charger;
0136
0137 lt3651_charger->charger = devm_power_supply_register(&pdev->dev,
0138 charger_desc, &psy_cfg);
0139 if (IS_ERR(lt3651_charger->charger)) {
0140 ret = PTR_ERR(lt3651_charger->charger);
0141 dev_err(&pdev->dev, "Failed to register power supply: %d\n",
0142 ret);
0143 return ret;
0144 }
0145
0146
0147
0148
0149
0150
0151 if (lt3651_charger->acpr_gpio) {
0152 ret = gpiod_to_irq(lt3651_charger->acpr_gpio);
0153 if (ret >= 0)
0154 ret = devm_request_any_context_irq(&pdev->dev, ret,
0155 lt3651_charger_irq,
0156 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
0157 dev_name(&pdev->dev), lt3651_charger->charger);
0158 if (ret < 0)
0159 dev_warn(&pdev->dev, "Failed to request acpr irq\n");
0160 }
0161 if (lt3651_charger->fault_gpio) {
0162 ret = gpiod_to_irq(lt3651_charger->fault_gpio);
0163 if (ret >= 0)
0164 ret = devm_request_any_context_irq(&pdev->dev, ret,
0165 lt3651_charger_irq,
0166 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
0167 dev_name(&pdev->dev), lt3651_charger->charger);
0168 if (ret < 0)
0169 dev_warn(&pdev->dev, "Failed to request fault irq\n");
0170 }
0171 if (lt3651_charger->chrg_gpio) {
0172 ret = gpiod_to_irq(lt3651_charger->chrg_gpio);
0173 if (ret >= 0)
0174 ret = devm_request_any_context_irq(&pdev->dev, ret,
0175 lt3651_charger_irq,
0176 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
0177 dev_name(&pdev->dev), lt3651_charger->charger);
0178 if (ret < 0)
0179 dev_warn(&pdev->dev, "Failed to request chrg irq\n");
0180 }
0181
0182 platform_set_drvdata(pdev, lt3651_charger);
0183
0184 return 0;
0185 }
0186
0187 static const struct of_device_id lt3651_charger_match[] = {
0188 { .compatible = "lltc,ltc3651-charger" },
0189 { .compatible = "lltc,lt3651-charger" },
0190 { }
0191 };
0192 MODULE_DEVICE_TABLE(of, lt3651_charger_match);
0193
0194 static struct platform_driver lt3651_charger_driver = {
0195 .probe = lt3651_charger_probe,
0196 .driver = {
0197 .name = "lt3651-charger",
0198 .of_match_table = lt3651_charger_match,
0199 },
0200 };
0201
0202 module_platform_driver(lt3651_charger_driver);
0203
0204 MODULE_AUTHOR("Mike Looijmans <mike.looijmans@topic.nl>");
0205 MODULE_DESCRIPTION("Driver for LT3651 charger");
0206 MODULE_LICENSE("GPL");
0207 MODULE_ALIAS("platform:lt3651-charger");