191937b14SEnric Balletbo i Serra // SPDX-License-Identifier: GPL-2.0
291937b14SEnric Balletbo i Serra // Battery charger driver for TI's tps65217
391937b14SEnric Balletbo i Serra //
491937b14SEnric Balletbo i Serra // Copyright (C) 2015 Collabora Ltd.
591937b14SEnric Balletbo i Serra // Author: Enric Balletbo i Serra <enric.balletbo@collabora.com>
68c0984e5SSebastian Reichel
78c0984e5SSebastian Reichel /*
88c0984e5SSebastian Reichel * Battery charger driver for TI's tps65217
98c0984e5SSebastian Reichel */
108c0984e5SSebastian Reichel #include <linux/kernel.h>
118c0984e5SSebastian Reichel #include <linux/kthread.h>
128c0984e5SSebastian Reichel #include <linux/device.h>
138c0984e5SSebastian Reichel #include <linux/module.h>
148c0984e5SSebastian Reichel #include <linux/platform_device.h>
158c0984e5SSebastian Reichel #include <linux/init.h>
168c0984e5SSebastian Reichel #include <linux/interrupt.h>
178c0984e5SSebastian Reichel #include <linux/slab.h>
188c0984e5SSebastian Reichel #include <linux/err.h>
198c0984e5SSebastian Reichel #include <linux/of.h>
208c0984e5SSebastian Reichel #include <linux/power_supply.h>
218c0984e5SSebastian Reichel
228c0984e5SSebastian Reichel #include <linux/mfd/core.h>
238c0984e5SSebastian Reichel #include <linux/mfd/tps65217.h>
248c0984e5SSebastian Reichel
2520a7e173SMilo Kim #define CHARGER_STATUS_PRESENT (TPS65217_STATUS_ACPWR | TPS65217_STATUS_USBPWR)
2620a7e173SMilo Kim #define NUM_CHARGER_IRQS 2
278c0984e5SSebastian Reichel #define POLL_INTERVAL (HZ * 2)
288c0984e5SSebastian Reichel
298c0984e5SSebastian Reichel struct tps65217_charger {
308c0984e5SSebastian Reichel struct tps65217 *tps;
318c0984e5SSebastian Reichel struct device *dev;
323967d1f9SMilo Kim struct power_supply *psy;
338c0984e5SSebastian Reichel
343c2e58a6SMilo Kim int online;
353c2e58a6SMilo Kim int prev_online;
368c0984e5SSebastian Reichel
378c0984e5SSebastian Reichel struct task_struct *poll_task;
388c0984e5SSebastian Reichel };
398c0984e5SSebastian Reichel
40da50b3a5SMilo Kim static enum power_supply_property tps65217_charger_props[] = {
418c0984e5SSebastian Reichel POWER_SUPPLY_PROP_ONLINE,
428c0984e5SSebastian Reichel };
438c0984e5SSebastian Reichel
tps65217_config_charger(struct tps65217_charger * charger)448c0984e5SSebastian Reichel static int tps65217_config_charger(struct tps65217_charger *charger)
458c0984e5SSebastian Reichel {
468c0984e5SSebastian Reichel int ret;
478c0984e5SSebastian Reichel
488c0984e5SSebastian Reichel /*
498c0984e5SSebastian Reichel * tps65217 rev. G, p. 31 (see p. 32 for NTC schematic)
508c0984e5SSebastian Reichel *
518c0984e5SSebastian Reichel * The device can be configured to support a 100k NTC (B = 3960) by
523eb7508dSShaomin Deng * setting the NTC_TYPE bit in register CHGCONFIG1 to 1. However it
538c0984e5SSebastian Reichel * is not recommended to do so. In sleep mode, the charger continues
548c0984e5SSebastian Reichel * charging the battery, but all register values are reset to default
558c0984e5SSebastian Reichel * values. Therefore, the charger would get the wrong temperature
568c0984e5SSebastian Reichel * information. If 100k NTC setting is required, please contact the
578c0984e5SSebastian Reichel * factory.
588c0984e5SSebastian Reichel *
598c0984e5SSebastian Reichel * ATTENTION, conflicting information, from p. 46
608c0984e5SSebastian Reichel *
618c0984e5SSebastian Reichel * NTC TYPE (for battery temperature measurement)
628c0984e5SSebastian Reichel * 0 – 100k (curve 1, B = 3960)
638c0984e5SSebastian Reichel * 1 – 10k (curve 2, B = 3480) (default on reset)
648c0984e5SSebastian Reichel *
658c0984e5SSebastian Reichel */
668c0984e5SSebastian Reichel ret = tps65217_clear_bits(charger->tps, TPS65217_REG_CHGCONFIG1,
678c0984e5SSebastian Reichel TPS65217_CHGCONFIG1_NTC_TYPE,
688c0984e5SSebastian Reichel TPS65217_PROTECT_NONE);
698c0984e5SSebastian Reichel if (ret) {
708c0984e5SSebastian Reichel dev_err(charger->dev,
718c0984e5SSebastian Reichel "failed to set 100k NTC setting: %d\n", ret);
728c0984e5SSebastian Reichel return ret;
738c0984e5SSebastian Reichel }
748c0984e5SSebastian Reichel
758c0984e5SSebastian Reichel return 0;
768c0984e5SSebastian Reichel }
778c0984e5SSebastian Reichel
tps65217_enable_charging(struct tps65217_charger * charger)788c0984e5SSebastian Reichel static int tps65217_enable_charging(struct tps65217_charger *charger)
798c0984e5SSebastian Reichel {
808c0984e5SSebastian Reichel int ret;
818c0984e5SSebastian Reichel
828c0984e5SSebastian Reichel /* charger already enabled */
833c2e58a6SMilo Kim if (charger->online)
848c0984e5SSebastian Reichel return 0;
858c0984e5SSebastian Reichel
868c0984e5SSebastian Reichel dev_dbg(charger->dev, "%s: enable charging\n", __func__);
878c0984e5SSebastian Reichel ret = tps65217_set_bits(charger->tps, TPS65217_REG_CHGCONFIG1,
888c0984e5SSebastian Reichel TPS65217_CHGCONFIG1_CHG_EN,
898c0984e5SSebastian Reichel TPS65217_CHGCONFIG1_CHG_EN,
908c0984e5SSebastian Reichel TPS65217_PROTECT_NONE);
918c0984e5SSebastian Reichel if (ret) {
928c0984e5SSebastian Reichel dev_err(charger->dev,
938c0984e5SSebastian Reichel "%s: Error in writing CHG_EN in reg 0x%x: %d\n",
948c0984e5SSebastian Reichel __func__, TPS65217_REG_CHGCONFIG1, ret);
958c0984e5SSebastian Reichel return ret;
968c0984e5SSebastian Reichel }
978c0984e5SSebastian Reichel
983c2e58a6SMilo Kim charger->online = 1;
998c0984e5SSebastian Reichel
1008c0984e5SSebastian Reichel return 0;
1018c0984e5SSebastian Reichel }
1028c0984e5SSebastian Reichel
tps65217_charger_get_property(struct power_supply * psy,enum power_supply_property psp,union power_supply_propval * val)103757620c4SMilo Kim static int tps65217_charger_get_property(struct power_supply *psy,
1048c0984e5SSebastian Reichel enum power_supply_property psp,
1058c0984e5SSebastian Reichel union power_supply_propval *val)
1068c0984e5SSebastian Reichel {
1078c0984e5SSebastian Reichel struct tps65217_charger *charger = power_supply_get_drvdata(psy);
1088c0984e5SSebastian Reichel
1098c0984e5SSebastian Reichel if (psp == POWER_SUPPLY_PROP_ONLINE) {
1103c2e58a6SMilo Kim val->intval = charger->online;
1118c0984e5SSebastian Reichel return 0;
1128c0984e5SSebastian Reichel }
1138c0984e5SSebastian Reichel return -EINVAL;
1148c0984e5SSebastian Reichel }
1158c0984e5SSebastian Reichel
tps65217_charger_irq(int irq,void * dev)1168c0984e5SSebastian Reichel static irqreturn_t tps65217_charger_irq(int irq, void *dev)
1178c0984e5SSebastian Reichel {
1188c0984e5SSebastian Reichel int ret, val;
1198c0984e5SSebastian Reichel struct tps65217_charger *charger = dev;
1208c0984e5SSebastian Reichel
1213c2e58a6SMilo Kim charger->prev_online = charger->online;
1228c0984e5SSebastian Reichel
1238c0984e5SSebastian Reichel ret = tps65217_reg_read(charger->tps, TPS65217_REG_STATUS, &val);
1248c0984e5SSebastian Reichel if (ret < 0) {
1258c0984e5SSebastian Reichel dev_err(charger->dev, "%s: Error in reading reg 0x%x\n",
1268c0984e5SSebastian Reichel __func__, TPS65217_REG_STATUS);
1278c0984e5SSebastian Reichel return IRQ_HANDLED;
1288c0984e5SSebastian Reichel }
1298c0984e5SSebastian Reichel
1308c0984e5SSebastian Reichel dev_dbg(charger->dev, "%s: 0x%x\n", __func__, val);
1318c0984e5SSebastian Reichel
13220a7e173SMilo Kim /* check for charger status bit */
13320a7e173SMilo Kim if (val & CHARGER_STATUS_PRESENT) {
1348c0984e5SSebastian Reichel ret = tps65217_enable_charging(charger);
1358c0984e5SSebastian Reichel if (ret) {
1368c0984e5SSebastian Reichel dev_err(charger->dev,
1378c0984e5SSebastian Reichel "failed to enable charger: %d\n", ret);
1388c0984e5SSebastian Reichel return IRQ_HANDLED;
1398c0984e5SSebastian Reichel }
1408c0984e5SSebastian Reichel } else {
1413c2e58a6SMilo Kim charger->online = 0;
1428c0984e5SSebastian Reichel }
1438c0984e5SSebastian Reichel
1443c2e58a6SMilo Kim if (charger->prev_online != charger->online)
1453967d1f9SMilo Kim power_supply_changed(charger->psy);
1468c0984e5SSebastian Reichel
1478c0984e5SSebastian Reichel ret = tps65217_reg_read(charger->tps, TPS65217_REG_CHGCONFIG0, &val);
1488c0984e5SSebastian Reichel if (ret < 0) {
1498c0984e5SSebastian Reichel dev_err(charger->dev, "%s: Error in reading reg 0x%x\n",
1508c0984e5SSebastian Reichel __func__, TPS65217_REG_CHGCONFIG0);
1518c0984e5SSebastian Reichel return IRQ_HANDLED;
1528c0984e5SSebastian Reichel }
1538c0984e5SSebastian Reichel
1548c0984e5SSebastian Reichel if (val & TPS65217_CHGCONFIG0_ACTIVE)
1558c0984e5SSebastian Reichel dev_dbg(charger->dev, "%s: charger is charging\n", __func__);
1568c0984e5SSebastian Reichel else
1578c0984e5SSebastian Reichel dev_dbg(charger->dev,
1588c0984e5SSebastian Reichel "%s: charger is NOT charging\n", __func__);
1598c0984e5SSebastian Reichel
1608c0984e5SSebastian Reichel return IRQ_HANDLED;
1618c0984e5SSebastian Reichel }
1628c0984e5SSebastian Reichel
tps65217_charger_poll_task(void * data)1638c0984e5SSebastian Reichel static int tps65217_charger_poll_task(void *data)
1648c0984e5SSebastian Reichel {
1658c0984e5SSebastian Reichel set_freezable();
1668c0984e5SSebastian Reichel
1678c0984e5SSebastian Reichel while (!kthread_should_stop()) {
1688c0984e5SSebastian Reichel schedule_timeout_interruptible(POLL_INTERVAL);
1698c0984e5SSebastian Reichel try_to_freeze();
1708c0984e5SSebastian Reichel tps65217_charger_irq(-1, data);
1718c0984e5SSebastian Reichel }
1728c0984e5SSebastian Reichel return 0;
1738c0984e5SSebastian Reichel }
1748c0984e5SSebastian Reichel
1758c0984e5SSebastian Reichel static const struct power_supply_desc tps65217_charger_desc = {
1765b903a15SMilo Kim .name = "tps65217-charger",
1778c0984e5SSebastian Reichel .type = POWER_SUPPLY_TYPE_MAINS,
178757620c4SMilo Kim .get_property = tps65217_charger_get_property,
179da50b3a5SMilo Kim .properties = tps65217_charger_props,
180da50b3a5SMilo Kim .num_properties = ARRAY_SIZE(tps65217_charger_props),
1818c0984e5SSebastian Reichel };
1828c0984e5SSebastian Reichel
tps65217_charger_probe(struct platform_device * pdev)1838c0984e5SSebastian Reichel static int tps65217_charger_probe(struct platform_device *pdev)
1848c0984e5SSebastian Reichel {
1858c0984e5SSebastian Reichel struct tps65217 *tps = dev_get_drvdata(pdev->dev.parent);
1868c0984e5SSebastian Reichel struct tps65217_charger *charger;
1878c0984e5SSebastian Reichel struct power_supply_config cfg = {};
1889ef0bf11SMilo Kim struct task_struct *poll_task;
18920a7e173SMilo Kim int irq[NUM_CHARGER_IRQS];
1908c0984e5SSebastian Reichel int ret;
19120a7e173SMilo Kim int i;
1928c0984e5SSebastian Reichel
1938c0984e5SSebastian Reichel charger = devm_kzalloc(&pdev->dev, sizeof(*charger), GFP_KERNEL);
1948c0984e5SSebastian Reichel if (!charger)
1958c0984e5SSebastian Reichel return -ENOMEM;
1968c0984e5SSebastian Reichel
197f7c8f1deSSebastian Reichel platform_set_drvdata(pdev, charger);
1988c0984e5SSebastian Reichel charger->tps = tps;
1998c0984e5SSebastian Reichel charger->dev = &pdev->dev;
2008c0984e5SSebastian Reichel
2018c0984e5SSebastian Reichel cfg.of_node = pdev->dev.of_node;
2028c0984e5SSebastian Reichel cfg.drv_data = charger;
2038c0984e5SSebastian Reichel
2043967d1f9SMilo Kim charger->psy = devm_power_supply_register(&pdev->dev,
2058c0984e5SSebastian Reichel &tps65217_charger_desc,
2068c0984e5SSebastian Reichel &cfg);
2073967d1f9SMilo Kim if (IS_ERR(charger->psy)) {
2088c0984e5SSebastian Reichel dev_err(&pdev->dev, "failed: power supply register\n");
2093967d1f9SMilo Kim return PTR_ERR(charger->psy);
2108c0984e5SSebastian Reichel }
2118c0984e5SSebastian Reichel
21220a7e173SMilo Kim irq[0] = platform_get_irq_byname(pdev, "USB");
21320a7e173SMilo Kim irq[1] = platform_get_irq_byname(pdev, "AC");
21447d7d5edSMarcin Niestroj
2158c0984e5SSebastian Reichel ret = tps65217_config_charger(charger);
2168c0984e5SSebastian Reichel if (ret < 0) {
2178c0984e5SSebastian Reichel dev_err(charger->dev, "charger config failed, err %d\n", ret);
2188c0984e5SSebastian Reichel return ret;
2198c0984e5SSebastian Reichel }
2208c0984e5SSebastian Reichel
22120a7e173SMilo Kim /* Create a polling thread if an interrupt is invalid */
22220a7e173SMilo Kim if (irq[0] < 0 || irq[1] < 0) {
2239ef0bf11SMilo Kim poll_task = kthread_run(tps65217_charger_poll_task,
2248c0984e5SSebastian Reichel charger, "ktps65217charger");
2259ef0bf11SMilo Kim if (IS_ERR(poll_task)) {
2269ef0bf11SMilo Kim ret = PTR_ERR(poll_task);
22747d7d5edSMarcin Niestroj dev_err(charger->dev,
22847d7d5edSMarcin Niestroj "Unable to run kthread err %d\n", ret);
2298c0984e5SSebastian Reichel return ret;
2308c0984e5SSebastian Reichel }
2319ef0bf11SMilo Kim
2329ef0bf11SMilo Kim charger->poll_task = poll_task;
23320a7e173SMilo Kim return 0;
23420a7e173SMilo Kim }
23520a7e173SMilo Kim
23620a7e173SMilo Kim /* Create IRQ threads for charger interrupts */
23720a7e173SMilo Kim for (i = 0; i < NUM_CHARGER_IRQS; i++) {
23820a7e173SMilo Kim ret = devm_request_threaded_irq(&pdev->dev, irq[i], NULL,
23920a7e173SMilo Kim tps65217_charger_irq,
240*ea17be9dSGrant B Adams IRQF_SHARED, "tps65217-charger",
24120a7e173SMilo Kim charger);
24220a7e173SMilo Kim if (ret) {
24320a7e173SMilo Kim dev_err(charger->dev,
24420a7e173SMilo Kim "Unable to register irq %d err %d\n", irq[i],
24520a7e173SMilo Kim ret);
24620a7e173SMilo Kim return ret;
24720a7e173SMilo Kim }
24820a7e173SMilo Kim
24920a7e173SMilo Kim /* Check current state */
25020a7e173SMilo Kim tps65217_charger_irq(-1, charger);
25147d7d5edSMarcin Niestroj }
2528c0984e5SSebastian Reichel
2538c0984e5SSebastian Reichel return 0;
2548c0984e5SSebastian Reichel }
2558c0984e5SSebastian Reichel
tps65217_charger_remove(struct platform_device * pdev)25607a93989SUwe Kleine-König static void tps65217_charger_remove(struct platform_device *pdev)
2578c0984e5SSebastian Reichel {
2588c0984e5SSebastian Reichel struct tps65217_charger *charger = platform_get_drvdata(pdev);
2598c0984e5SSebastian Reichel
2609ef0bf11SMilo Kim if (charger->poll_task)
2618c0984e5SSebastian Reichel kthread_stop(charger->poll_task);
2628c0984e5SSebastian Reichel }
2638c0984e5SSebastian Reichel
2648c0984e5SSebastian Reichel static const struct of_device_id tps65217_charger_match_table[] = {
2658c0984e5SSebastian Reichel { .compatible = "ti,tps65217-charger", },
2668c0984e5SSebastian Reichel { /* sentinel */ }
2678c0984e5SSebastian Reichel };
2688c0984e5SSebastian Reichel MODULE_DEVICE_TABLE(of, tps65217_charger_match_table);
2698c0984e5SSebastian Reichel
2708c0984e5SSebastian Reichel static struct platform_driver tps65217_charger_driver = {
2718c0984e5SSebastian Reichel .probe = tps65217_charger_probe,
27207a93989SUwe Kleine-König .remove_new = tps65217_charger_remove,
2738c0984e5SSebastian Reichel .driver = {
2748c0984e5SSebastian Reichel .name = "tps65217-charger",
2758c0984e5SSebastian Reichel .of_match_table = of_match_ptr(tps65217_charger_match_table),
2768c0984e5SSebastian Reichel },
2778c0984e5SSebastian Reichel
2788c0984e5SSebastian Reichel };
2798c0984e5SSebastian Reichel module_platform_driver(tps65217_charger_driver);
2808c0984e5SSebastian Reichel
2818c0984e5SSebastian Reichel MODULE_LICENSE("GPL v2");
2828c0984e5SSebastian Reichel MODULE_AUTHOR("Enric Balletbo Serra <enric.balletbo@collabora.com>");
2838c0984e5SSebastian Reichel MODULE_DESCRIPTION("TPS65217 battery charger driver");
284