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.5-rc3 264 lines 6.8 kB view raw
1/* 2 * Battery charger driver for TI's tps65217 3 * 4 * Copyright (c) 2015, Collabora Ltd. 5 6 * This program is free software; you can redistribute it and/or modify it 7 * under the terms and conditions of the GNU General Public License, 8 * version 2, as published by the Free Software Foundation. 9 10 * This program is distributed in the hope it will be useful, but WITHOUT 11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 * more details. 14 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 */ 18 19/* 20 * Battery charger driver for TI's tps65217 21 */ 22#include <linux/kernel.h> 23#include <linux/kthread.h> 24#include <linux/device.h> 25#include <linux/module.h> 26#include <linux/platform_device.h> 27#include <linux/init.h> 28#include <linux/interrupt.h> 29#include <linux/slab.h> 30#include <linux/err.h> 31#include <linux/of.h> 32#include <linux/of_device.h> 33#include <linux/power_supply.h> 34 35#include <linux/mfd/core.h> 36#include <linux/mfd/tps65217.h> 37 38#define POLL_INTERVAL (HZ * 2) 39 40struct tps65217_charger { 41 struct tps65217 *tps; 42 struct device *dev; 43 struct power_supply *ac; 44 45 int ac_online; 46 int prev_ac_online; 47 48 struct task_struct *poll_task; 49}; 50 51static enum power_supply_property tps65217_ac_props[] = { 52 POWER_SUPPLY_PROP_ONLINE, 53}; 54 55static int tps65217_config_charger(struct tps65217_charger *charger) 56{ 57 int ret; 58 59 dev_dbg(charger->dev, "%s\n", __func__); 60 61 /* 62 * tps65217 rev. G, p. 31 (see p. 32 for NTC schematic) 63 * 64 * The device can be configured to support a 100k NTC (B = 3960) by 65 * setting the the NTC_TYPE bit in register CHGCONFIG1 to 1. However it 66 * is not recommended to do so. In sleep mode, the charger continues 67 * charging the battery, but all register values are reset to default 68 * values. Therefore, the charger would get the wrong temperature 69 * information. If 100k NTC setting is required, please contact the 70 * factory. 71 * 72 * ATTENTION, conflicting information, from p. 46 73 * 74 * NTC TYPE (for battery temperature measurement) 75 * 0 – 100k (curve 1, B = 3960) 76 * 1 – 10k (curve 2, B = 3480) (default on reset) 77 * 78 */ 79 ret = tps65217_clear_bits(charger->tps, TPS65217_REG_CHGCONFIG1, 80 TPS65217_CHGCONFIG1_NTC_TYPE, 81 TPS65217_PROTECT_NONE); 82 if (ret) { 83 dev_err(charger->dev, 84 "failed to set 100k NTC setting: %d\n", ret); 85 return ret; 86 } 87 88 return 0; 89} 90 91static int tps65217_enable_charging(struct tps65217_charger *charger) 92{ 93 int ret; 94 95 /* charger already enabled */ 96 if (charger->ac_online) 97 return 0; 98 99 dev_dbg(charger->dev, "%s: enable charging\n", __func__); 100 ret = tps65217_set_bits(charger->tps, TPS65217_REG_CHGCONFIG1, 101 TPS65217_CHGCONFIG1_CHG_EN, 102 TPS65217_CHGCONFIG1_CHG_EN, 103 TPS65217_PROTECT_NONE); 104 if (ret) { 105 dev_err(charger->dev, 106 "%s: Error in writing CHG_EN in reg 0x%x: %d\n", 107 __func__, TPS65217_REG_CHGCONFIG1, ret); 108 return ret; 109 } 110 111 charger->ac_online = 1; 112 113 return 0; 114} 115 116static int tps65217_ac_get_property(struct power_supply *psy, 117 enum power_supply_property psp, 118 union power_supply_propval *val) 119{ 120 struct tps65217_charger *charger = power_supply_get_drvdata(psy); 121 122 if (psp == POWER_SUPPLY_PROP_ONLINE) { 123 val->intval = charger->ac_online; 124 return 0; 125 } 126 return -EINVAL; 127} 128 129static irqreturn_t tps65217_charger_irq(int irq, void *dev) 130{ 131 int ret, val; 132 struct tps65217_charger *charger = dev; 133 134 charger->prev_ac_online = charger->ac_online; 135 136 ret = tps65217_reg_read(charger->tps, TPS65217_REG_STATUS, &val); 137 if (ret < 0) { 138 dev_err(charger->dev, "%s: Error in reading reg 0x%x\n", 139 __func__, TPS65217_REG_STATUS); 140 return IRQ_HANDLED; 141 } 142 143 dev_dbg(charger->dev, "%s: 0x%x\n", __func__, val); 144 145 /* check for AC status bit */ 146 if (val & TPS65217_STATUS_ACPWR) { 147 ret = tps65217_enable_charging(charger); 148 if (ret) { 149 dev_err(charger->dev, 150 "failed to enable charger: %d\n", ret); 151 return IRQ_HANDLED; 152 } 153 } else { 154 charger->ac_online = 0; 155 } 156 157 if (charger->prev_ac_online != charger->ac_online) 158 power_supply_changed(charger->ac); 159 160 ret = tps65217_reg_read(charger->tps, TPS65217_REG_CHGCONFIG0, &val); 161 if (ret < 0) { 162 dev_err(charger->dev, "%s: Error in reading reg 0x%x\n", 163 __func__, TPS65217_REG_CHGCONFIG0); 164 return IRQ_HANDLED; 165 } 166 167 if (val & TPS65217_CHGCONFIG0_ACTIVE) 168 dev_dbg(charger->dev, "%s: charger is charging\n", __func__); 169 else 170 dev_dbg(charger->dev, 171 "%s: charger is NOT charging\n", __func__); 172 173 return IRQ_HANDLED; 174} 175 176static int tps65217_charger_poll_task(void *data) 177{ 178 set_freezable(); 179 180 while (!kthread_should_stop()) { 181 schedule_timeout_interruptible(POLL_INTERVAL); 182 try_to_freeze(); 183 tps65217_charger_irq(-1, data); 184 } 185 return 0; 186} 187 188static const struct power_supply_desc tps65217_charger_desc = { 189 .name = "tps65217-ac", 190 .type = POWER_SUPPLY_TYPE_MAINS, 191 .get_property = tps65217_ac_get_property, 192 .properties = tps65217_ac_props, 193 .num_properties = ARRAY_SIZE(tps65217_ac_props), 194}; 195 196static int tps65217_charger_probe(struct platform_device *pdev) 197{ 198 struct tps65217 *tps = dev_get_drvdata(pdev->dev.parent); 199 struct tps65217_charger *charger; 200 int ret; 201 202 dev_dbg(&pdev->dev, "%s\n", __func__); 203 204 charger = devm_kzalloc(&pdev->dev, sizeof(*charger), GFP_KERNEL); 205 if (!charger) 206 return -ENOMEM; 207 208 charger->tps = tps; 209 charger->dev = &pdev->dev; 210 211 charger->ac = devm_power_supply_register(&pdev->dev, 212 &tps65217_charger_desc, 213 NULL); 214 if (IS_ERR(charger->ac)) { 215 dev_err(&pdev->dev, "failed: power supply register\n"); 216 return PTR_ERR(charger->ac); 217 } 218 219 ret = tps65217_config_charger(charger); 220 if (ret < 0) { 221 dev_err(charger->dev, "charger config failed, err %d\n", ret); 222 return ret; 223 } 224 225 charger->poll_task = kthread_run(tps65217_charger_poll_task, 226 charger, "ktps65217charger"); 227 if (IS_ERR(charger->poll_task)) { 228 ret = PTR_ERR(charger->poll_task); 229 dev_err(charger->dev, "Unable to run kthread err %d\n", ret); 230 return ret; 231 } 232 233 return 0; 234} 235 236static int tps65217_charger_remove(struct platform_device *pdev) 237{ 238 struct tps65217_charger *charger = platform_get_drvdata(pdev); 239 240 kthread_stop(charger->poll_task); 241 242 return 0; 243} 244 245static const struct of_device_id tps65217_charger_match_table[] = { 246 { .compatible = "ti,tps65217-charger", }, 247 { /* sentinel */ } 248}; 249MODULE_DEVICE_TABLE(of, tps65217_charger_match_table); 250 251static struct platform_driver tps65217_charger_driver = { 252 .probe = tps65217_charger_probe, 253 .remove = tps65217_charger_remove, 254 .driver = { 255 .name = "tps65217-charger", 256 .of_match_table = of_match_ptr(tps65217_charger_match_table), 257 }, 258 259}; 260module_platform_driver(tps65217_charger_driver); 261 262MODULE_LICENSE("GPL v2"); 263MODULE_AUTHOR("Enric Balletbo Serra <enric.balletbo@collabora.com>"); 264MODULE_DESCRIPTION("TPS65217 battery charger driver");