284 lines · c
1// SPDX-License-Identifier: GPL-2.02// Battery charger driver for TI's tps652173//4// Copyright (C) 2015 Collabora Ltd.5// Author: Enric Balletbo i Serra <enric.balletbo@collabora.com>6 7/*8 * Battery charger driver for TI's tps652179 */10#include <linux/kernel.h>11#include <linux/kthread.h>12#include <linux/device.h>13#include <linux/module.h>14#include <linux/platform_device.h>15#include <linux/init.h>16#include <linux/interrupt.h>17#include <linux/slab.h>18#include <linux/err.h>19#include <linux/of.h>20#include <linux/power_supply.h>21 22#include <linux/mfd/core.h>23#include <linux/mfd/tps65217.h>24 25#define CHARGER_STATUS_PRESENT (TPS65217_STATUS_ACPWR | TPS65217_STATUS_USBPWR)26#define NUM_CHARGER_IRQS 227#define POLL_INTERVAL (HZ * 2)28 29struct tps65217_charger {30 struct tps65217 *tps;31 struct device *dev;32 struct power_supply *psy;33 34 int online;35 int prev_online;36 37 struct task_struct *poll_task;38};39 40static enum power_supply_property tps65217_charger_props[] = {41 POWER_SUPPLY_PROP_ONLINE,42};43 44static int tps65217_config_charger(struct tps65217_charger *charger)45{46 int ret;47 48 /*49 * tps65217 rev. G, p. 31 (see p. 32 for NTC schematic)50 *51 * The device can be configured to support a 100k NTC (B = 3960) by52 * setting the NTC_TYPE bit in register CHGCONFIG1 to 1. However it53 * is not recommended to do so. In sleep mode, the charger continues54 * charging the battery, but all register values are reset to default55 * values. Therefore, the charger would get the wrong temperature56 * information. If 100k NTC setting is required, please contact the57 * factory.58 *59 * ATTENTION, conflicting information, from p. 4660 *61 * NTC TYPE (for battery temperature measurement)62 * 0 – 100k (curve 1, B = 3960)63 * 1 – 10k (curve 2, B = 3480) (default on reset)64 *65 */66 ret = tps65217_clear_bits(charger->tps, TPS65217_REG_CHGCONFIG1,67 TPS65217_CHGCONFIG1_NTC_TYPE,68 TPS65217_PROTECT_NONE);69 if (ret) {70 dev_err(charger->dev,71 "failed to set 100k NTC setting: %d\n", ret);72 return ret;73 }74 75 return 0;76}77 78static int tps65217_enable_charging(struct tps65217_charger *charger)79{80 int ret;81 82 /* charger already enabled */83 if (charger->online)84 return 0;85 86 dev_dbg(charger->dev, "%s: enable charging\n", __func__);87 ret = tps65217_set_bits(charger->tps, TPS65217_REG_CHGCONFIG1,88 TPS65217_CHGCONFIG1_CHG_EN,89 TPS65217_CHGCONFIG1_CHG_EN,90 TPS65217_PROTECT_NONE);91 if (ret) {92 dev_err(charger->dev,93 "%s: Error in writing CHG_EN in reg 0x%x: %d\n",94 __func__, TPS65217_REG_CHGCONFIG1, ret);95 return ret;96 }97 98 charger->online = 1;99 100 return 0;101}102 103static int tps65217_charger_get_property(struct power_supply *psy,104 enum power_supply_property psp,105 union power_supply_propval *val)106{107 struct tps65217_charger *charger = power_supply_get_drvdata(psy);108 109 if (psp == POWER_SUPPLY_PROP_ONLINE) {110 val->intval = charger->online;111 return 0;112 }113 return -EINVAL;114}115 116static irqreturn_t tps65217_charger_irq(int irq, void *dev)117{118 int ret, val;119 struct tps65217_charger *charger = dev;120 121 charger->prev_online = charger->online;122 123 ret = tps65217_reg_read(charger->tps, TPS65217_REG_STATUS, &val);124 if (ret < 0) {125 dev_err(charger->dev, "%s: Error in reading reg 0x%x\n",126 __func__, TPS65217_REG_STATUS);127 return IRQ_HANDLED;128 }129 130 dev_dbg(charger->dev, "%s: 0x%x\n", __func__, val);131 132 /* check for charger status bit */133 if (val & CHARGER_STATUS_PRESENT) {134 ret = tps65217_enable_charging(charger);135 if (ret) {136 dev_err(charger->dev,137 "failed to enable charger: %d\n", ret);138 return IRQ_HANDLED;139 }140 } else {141 charger->online = 0;142 }143 144 if (charger->prev_online != charger->online)145 power_supply_changed(charger->psy);146 147 ret = tps65217_reg_read(charger->tps, TPS65217_REG_CHGCONFIG0, &val);148 if (ret < 0) {149 dev_err(charger->dev, "%s: Error in reading reg 0x%x\n",150 __func__, TPS65217_REG_CHGCONFIG0);151 return IRQ_HANDLED;152 }153 154 if (val & TPS65217_CHGCONFIG0_ACTIVE)155 dev_dbg(charger->dev, "%s: charger is charging\n", __func__);156 else157 dev_dbg(charger->dev,158 "%s: charger is NOT charging\n", __func__);159 160 return IRQ_HANDLED;161}162 163static int tps65217_charger_poll_task(void *data)164{165 set_freezable();166 167 while (!kthread_should_stop()) {168 schedule_timeout_interruptible(POLL_INTERVAL);169 try_to_freeze();170 tps65217_charger_irq(-1, data);171 }172 return 0;173}174 175static const struct power_supply_desc tps65217_charger_desc = {176 .name = "tps65217-charger",177 .type = POWER_SUPPLY_TYPE_MAINS,178 .get_property = tps65217_charger_get_property,179 .properties = tps65217_charger_props,180 .num_properties = ARRAY_SIZE(tps65217_charger_props),181};182 183static int tps65217_charger_probe(struct platform_device *pdev)184{185 struct tps65217 *tps = dev_get_drvdata(pdev->dev.parent);186 struct tps65217_charger *charger;187 struct power_supply_config cfg = {};188 struct task_struct *poll_task;189 int irq[NUM_CHARGER_IRQS];190 int ret;191 int i;192 193 charger = devm_kzalloc(&pdev->dev, sizeof(*charger), GFP_KERNEL);194 if (!charger)195 return -ENOMEM;196 197 platform_set_drvdata(pdev, charger);198 charger->tps = tps;199 charger->dev = &pdev->dev;200 201 cfg.of_node = pdev->dev.of_node;202 cfg.drv_data = charger;203 204 charger->psy = devm_power_supply_register(&pdev->dev,205 &tps65217_charger_desc,206 &cfg);207 if (IS_ERR(charger->psy)) {208 dev_err(&pdev->dev, "failed: power supply register\n");209 return PTR_ERR(charger->psy);210 }211 212 irq[0] = platform_get_irq_byname(pdev, "USB");213 irq[1] = platform_get_irq_byname(pdev, "AC");214 215 ret = tps65217_config_charger(charger);216 if (ret < 0) {217 dev_err(charger->dev, "charger config failed, err %d\n", ret);218 return ret;219 }220 221 /* Create a polling thread if an interrupt is invalid */222 if (irq[0] < 0 || irq[1] < 0) {223 poll_task = kthread_run(tps65217_charger_poll_task,224 charger, "ktps65217charger");225 if (IS_ERR(poll_task)) {226 ret = PTR_ERR(poll_task);227 dev_err(charger->dev,228 "Unable to run kthread err %d\n", ret);229 return ret;230 }231 232 charger->poll_task = poll_task;233 return 0;234 }235 236 /* Create IRQ threads for charger interrupts */237 for (i = 0; i < NUM_CHARGER_IRQS; i++) {238 ret = devm_request_threaded_irq(&pdev->dev, irq[i], NULL,239 tps65217_charger_irq,240 IRQF_SHARED, "tps65217-charger",241 charger);242 if (ret) {243 dev_err(charger->dev,244 "Unable to register irq %d err %d\n", irq[i],245 ret);246 return ret;247 }248 249 /* Check current state */250 tps65217_charger_irq(-1, charger);251 }252 253 return 0;254}255 256static void tps65217_charger_remove(struct platform_device *pdev)257{258 struct tps65217_charger *charger = platform_get_drvdata(pdev);259 260 if (charger->poll_task)261 kthread_stop(charger->poll_task);262}263 264static const struct of_device_id tps65217_charger_match_table[] = {265 { .compatible = "ti,tps65217-charger", },266 { /* sentinel */ }267};268MODULE_DEVICE_TABLE(of, tps65217_charger_match_table);269 270static struct platform_driver tps65217_charger_driver = {271 .probe = tps65217_charger_probe,272 .remove_new = tps65217_charger_remove,273 .driver = {274 .name = "tps65217-charger",275 .of_match_table = of_match_ptr(tps65217_charger_match_table),276 },277 278};279module_platform_driver(tps65217_charger_driver);280 281MODULE_LICENSE("GPL v2");282MODULE_AUTHOR("Enric Balletbo Serra <enric.balletbo@collabora.com>");283MODULE_DESCRIPTION("TPS65217 battery charger driver");284