1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun * Battery charger driver for TI BQ24735
3*4882a593Smuzhiyun *
4*4882a593Smuzhiyun * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved.
5*4882a593Smuzhiyun *
6*4882a593Smuzhiyun * This program is free software; you can redistribute it and/or modify
7*4882a593Smuzhiyun * it under the terms of the GNU General Public License as published by
8*4882a593Smuzhiyun * the Free Software Foundation;
9*4882a593Smuzhiyun *
10*4882a593Smuzhiyun * This program is distributed in the hope that it will be useful, but WITHOUT
11*4882a593Smuzhiyun * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12*4882a593Smuzhiyun * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13*4882a593Smuzhiyun * more details.
14*4882a593Smuzhiyun *
15*4882a593Smuzhiyun * You should have received a copy of the GNU General Public License along
16*4882a593Smuzhiyun * with this program; if not, write to the Free Software Foundation, Inc.,
17*4882a593Smuzhiyun * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18*4882a593Smuzhiyun */
19*4882a593Smuzhiyun
20*4882a593Smuzhiyun #include <linux/err.h>
21*4882a593Smuzhiyun #include <linux/gpio.h>
22*4882a593Smuzhiyun #include <linux/i2c.h>
23*4882a593Smuzhiyun #include <linux/init.h>
24*4882a593Smuzhiyun #include <linux/interrupt.h>
25*4882a593Smuzhiyun #include <linux/kernel.h>
26*4882a593Smuzhiyun #include <linux/module.h>
27*4882a593Smuzhiyun #include <linux/of.h>
28*4882a593Smuzhiyun #include <linux/gpio/consumer.h>
29*4882a593Smuzhiyun #include <linux/power_supply.h>
30*4882a593Smuzhiyun #include <linux/slab.h>
31*4882a593Smuzhiyun
32*4882a593Smuzhiyun #include <linux/power/bq24735-charger.h>
33*4882a593Smuzhiyun
34*4882a593Smuzhiyun #define BQ24735_CHG_OPT 0x12
35*4882a593Smuzhiyun #define BQ24735_CHG_OPT_CHARGE_DISABLE (1 << 0)
36*4882a593Smuzhiyun #define BQ24735_CHG_OPT_AC_PRESENT (1 << 4)
37*4882a593Smuzhiyun #define BQ24735_CHARGE_CURRENT 0x14
38*4882a593Smuzhiyun #define BQ24735_CHARGE_CURRENT_MASK 0x1fc0
39*4882a593Smuzhiyun #define BQ24735_CHARGE_VOLTAGE 0x15
40*4882a593Smuzhiyun #define BQ24735_CHARGE_VOLTAGE_MASK 0x7ff0
41*4882a593Smuzhiyun #define BQ24735_INPUT_CURRENT 0x3f
42*4882a593Smuzhiyun #define BQ24735_INPUT_CURRENT_MASK 0x1f80
43*4882a593Smuzhiyun #define BQ24735_MANUFACTURER_ID 0xfe
44*4882a593Smuzhiyun #define BQ24735_DEVICE_ID 0xff
45*4882a593Smuzhiyun
46*4882a593Smuzhiyun struct bq24735 {
47*4882a593Smuzhiyun struct power_supply *charger;
48*4882a593Smuzhiyun struct power_supply_desc charger_desc;
49*4882a593Smuzhiyun struct i2c_client *client;
50*4882a593Smuzhiyun struct bq24735_platform *pdata;
51*4882a593Smuzhiyun struct mutex lock;
52*4882a593Smuzhiyun struct gpio_desc *status_gpio;
53*4882a593Smuzhiyun struct delayed_work poll;
54*4882a593Smuzhiyun u32 poll_interval;
55*4882a593Smuzhiyun bool charging;
56*4882a593Smuzhiyun };
57*4882a593Smuzhiyun
to_bq24735(struct power_supply * psy)58*4882a593Smuzhiyun static inline struct bq24735 *to_bq24735(struct power_supply *psy)
59*4882a593Smuzhiyun {
60*4882a593Smuzhiyun return power_supply_get_drvdata(psy);
61*4882a593Smuzhiyun }
62*4882a593Smuzhiyun
63*4882a593Smuzhiyun static enum power_supply_property bq24735_charger_properties[] = {
64*4882a593Smuzhiyun POWER_SUPPLY_PROP_STATUS,
65*4882a593Smuzhiyun POWER_SUPPLY_PROP_ONLINE,
66*4882a593Smuzhiyun };
67*4882a593Smuzhiyun
bq24735_charger_property_is_writeable(struct power_supply * psy,enum power_supply_property psp)68*4882a593Smuzhiyun static int bq24735_charger_property_is_writeable(struct power_supply *psy,
69*4882a593Smuzhiyun enum power_supply_property psp)
70*4882a593Smuzhiyun {
71*4882a593Smuzhiyun switch (psp) {
72*4882a593Smuzhiyun case POWER_SUPPLY_PROP_STATUS:
73*4882a593Smuzhiyun return 1;
74*4882a593Smuzhiyun default:
75*4882a593Smuzhiyun break;
76*4882a593Smuzhiyun }
77*4882a593Smuzhiyun
78*4882a593Smuzhiyun return 0;
79*4882a593Smuzhiyun }
80*4882a593Smuzhiyun
bq24735_write_word(struct i2c_client * client,u8 reg,u16 value)81*4882a593Smuzhiyun static inline int bq24735_write_word(struct i2c_client *client, u8 reg,
82*4882a593Smuzhiyun u16 value)
83*4882a593Smuzhiyun {
84*4882a593Smuzhiyun return i2c_smbus_write_word_data(client, reg, value);
85*4882a593Smuzhiyun }
86*4882a593Smuzhiyun
bq24735_read_word(struct i2c_client * client,u8 reg)87*4882a593Smuzhiyun static inline int bq24735_read_word(struct i2c_client *client, u8 reg)
88*4882a593Smuzhiyun {
89*4882a593Smuzhiyun return i2c_smbus_read_word_data(client, reg);
90*4882a593Smuzhiyun }
91*4882a593Smuzhiyun
bq24735_update_word(struct i2c_client * client,u8 reg,u16 mask,u16 value)92*4882a593Smuzhiyun static int bq24735_update_word(struct i2c_client *client, u8 reg,
93*4882a593Smuzhiyun u16 mask, u16 value)
94*4882a593Smuzhiyun {
95*4882a593Smuzhiyun unsigned int tmp;
96*4882a593Smuzhiyun int ret;
97*4882a593Smuzhiyun
98*4882a593Smuzhiyun ret = bq24735_read_word(client, reg);
99*4882a593Smuzhiyun if (ret < 0)
100*4882a593Smuzhiyun return ret;
101*4882a593Smuzhiyun
102*4882a593Smuzhiyun tmp = ret & ~mask;
103*4882a593Smuzhiyun tmp |= value & mask;
104*4882a593Smuzhiyun
105*4882a593Smuzhiyun return bq24735_write_word(client, reg, tmp);
106*4882a593Smuzhiyun }
107*4882a593Smuzhiyun
bq24735_config_charger(struct bq24735 * charger)108*4882a593Smuzhiyun static int bq24735_config_charger(struct bq24735 *charger)
109*4882a593Smuzhiyun {
110*4882a593Smuzhiyun struct bq24735_platform *pdata = charger->pdata;
111*4882a593Smuzhiyun int ret;
112*4882a593Smuzhiyun u16 value;
113*4882a593Smuzhiyun
114*4882a593Smuzhiyun if (pdata->ext_control)
115*4882a593Smuzhiyun return 0;
116*4882a593Smuzhiyun
117*4882a593Smuzhiyun if (pdata->charge_current) {
118*4882a593Smuzhiyun value = pdata->charge_current & BQ24735_CHARGE_CURRENT_MASK;
119*4882a593Smuzhiyun
120*4882a593Smuzhiyun ret = bq24735_write_word(charger->client,
121*4882a593Smuzhiyun BQ24735_CHARGE_CURRENT, value);
122*4882a593Smuzhiyun if (ret < 0) {
123*4882a593Smuzhiyun dev_err(&charger->client->dev,
124*4882a593Smuzhiyun "Failed to write charger current : %d\n",
125*4882a593Smuzhiyun ret);
126*4882a593Smuzhiyun return ret;
127*4882a593Smuzhiyun }
128*4882a593Smuzhiyun }
129*4882a593Smuzhiyun
130*4882a593Smuzhiyun if (pdata->charge_voltage) {
131*4882a593Smuzhiyun value = pdata->charge_voltage & BQ24735_CHARGE_VOLTAGE_MASK;
132*4882a593Smuzhiyun
133*4882a593Smuzhiyun ret = bq24735_write_word(charger->client,
134*4882a593Smuzhiyun BQ24735_CHARGE_VOLTAGE, value);
135*4882a593Smuzhiyun if (ret < 0) {
136*4882a593Smuzhiyun dev_err(&charger->client->dev,
137*4882a593Smuzhiyun "Failed to write charger voltage : %d\n",
138*4882a593Smuzhiyun ret);
139*4882a593Smuzhiyun return ret;
140*4882a593Smuzhiyun }
141*4882a593Smuzhiyun }
142*4882a593Smuzhiyun
143*4882a593Smuzhiyun if (pdata->input_current) {
144*4882a593Smuzhiyun value = pdata->input_current & BQ24735_INPUT_CURRENT_MASK;
145*4882a593Smuzhiyun
146*4882a593Smuzhiyun ret = bq24735_write_word(charger->client,
147*4882a593Smuzhiyun BQ24735_INPUT_CURRENT, value);
148*4882a593Smuzhiyun if (ret < 0) {
149*4882a593Smuzhiyun dev_err(&charger->client->dev,
150*4882a593Smuzhiyun "Failed to write input current : %d\n",
151*4882a593Smuzhiyun ret);
152*4882a593Smuzhiyun return ret;
153*4882a593Smuzhiyun }
154*4882a593Smuzhiyun }
155*4882a593Smuzhiyun
156*4882a593Smuzhiyun return 0;
157*4882a593Smuzhiyun }
158*4882a593Smuzhiyun
bq24735_enable_charging(struct bq24735 * charger)159*4882a593Smuzhiyun static inline int bq24735_enable_charging(struct bq24735 *charger)
160*4882a593Smuzhiyun {
161*4882a593Smuzhiyun int ret;
162*4882a593Smuzhiyun
163*4882a593Smuzhiyun if (charger->pdata->ext_control)
164*4882a593Smuzhiyun return 0;
165*4882a593Smuzhiyun
166*4882a593Smuzhiyun ret = bq24735_config_charger(charger);
167*4882a593Smuzhiyun if (ret)
168*4882a593Smuzhiyun return ret;
169*4882a593Smuzhiyun
170*4882a593Smuzhiyun return bq24735_update_word(charger->client, BQ24735_CHG_OPT,
171*4882a593Smuzhiyun BQ24735_CHG_OPT_CHARGE_DISABLE, 0);
172*4882a593Smuzhiyun }
173*4882a593Smuzhiyun
bq24735_disable_charging(struct bq24735 * charger)174*4882a593Smuzhiyun static inline int bq24735_disable_charging(struct bq24735 *charger)
175*4882a593Smuzhiyun {
176*4882a593Smuzhiyun if (charger->pdata->ext_control)
177*4882a593Smuzhiyun return 0;
178*4882a593Smuzhiyun
179*4882a593Smuzhiyun return bq24735_update_word(charger->client, BQ24735_CHG_OPT,
180*4882a593Smuzhiyun BQ24735_CHG_OPT_CHARGE_DISABLE,
181*4882a593Smuzhiyun BQ24735_CHG_OPT_CHARGE_DISABLE);
182*4882a593Smuzhiyun }
183*4882a593Smuzhiyun
bq24735_charger_is_present(struct bq24735 * charger)184*4882a593Smuzhiyun static bool bq24735_charger_is_present(struct bq24735 *charger)
185*4882a593Smuzhiyun {
186*4882a593Smuzhiyun if (charger->status_gpio) {
187*4882a593Smuzhiyun return !gpiod_get_value_cansleep(charger->status_gpio);
188*4882a593Smuzhiyun } else {
189*4882a593Smuzhiyun int ac = 0;
190*4882a593Smuzhiyun
191*4882a593Smuzhiyun ac = bq24735_read_word(charger->client, BQ24735_CHG_OPT);
192*4882a593Smuzhiyun if (ac < 0) {
193*4882a593Smuzhiyun dev_dbg(&charger->client->dev,
194*4882a593Smuzhiyun "Failed to read charger options : %d\n",
195*4882a593Smuzhiyun ac);
196*4882a593Smuzhiyun return false;
197*4882a593Smuzhiyun }
198*4882a593Smuzhiyun return (ac & BQ24735_CHG_OPT_AC_PRESENT) ? true : false;
199*4882a593Smuzhiyun }
200*4882a593Smuzhiyun
201*4882a593Smuzhiyun return false;
202*4882a593Smuzhiyun }
203*4882a593Smuzhiyun
bq24735_charger_is_charging(struct bq24735 * charger)204*4882a593Smuzhiyun static int bq24735_charger_is_charging(struct bq24735 *charger)
205*4882a593Smuzhiyun {
206*4882a593Smuzhiyun int ret;
207*4882a593Smuzhiyun
208*4882a593Smuzhiyun if (!bq24735_charger_is_present(charger))
209*4882a593Smuzhiyun return 0;
210*4882a593Smuzhiyun
211*4882a593Smuzhiyun ret = bq24735_read_word(charger->client, BQ24735_CHG_OPT);
212*4882a593Smuzhiyun if (ret < 0)
213*4882a593Smuzhiyun return ret;
214*4882a593Smuzhiyun
215*4882a593Smuzhiyun return !(ret & BQ24735_CHG_OPT_CHARGE_DISABLE);
216*4882a593Smuzhiyun }
217*4882a593Smuzhiyun
bq24735_update(struct bq24735 * charger)218*4882a593Smuzhiyun static void bq24735_update(struct bq24735 *charger)
219*4882a593Smuzhiyun {
220*4882a593Smuzhiyun mutex_lock(&charger->lock);
221*4882a593Smuzhiyun
222*4882a593Smuzhiyun if (charger->charging && bq24735_charger_is_present(charger))
223*4882a593Smuzhiyun bq24735_enable_charging(charger);
224*4882a593Smuzhiyun else
225*4882a593Smuzhiyun bq24735_disable_charging(charger);
226*4882a593Smuzhiyun
227*4882a593Smuzhiyun mutex_unlock(&charger->lock);
228*4882a593Smuzhiyun
229*4882a593Smuzhiyun power_supply_changed(charger->charger);
230*4882a593Smuzhiyun }
231*4882a593Smuzhiyun
bq24735_charger_isr(int irq,void * devid)232*4882a593Smuzhiyun static irqreturn_t bq24735_charger_isr(int irq, void *devid)
233*4882a593Smuzhiyun {
234*4882a593Smuzhiyun struct power_supply *psy = devid;
235*4882a593Smuzhiyun struct bq24735 *charger = to_bq24735(psy);
236*4882a593Smuzhiyun
237*4882a593Smuzhiyun bq24735_update(charger);
238*4882a593Smuzhiyun
239*4882a593Smuzhiyun return IRQ_HANDLED;
240*4882a593Smuzhiyun }
241*4882a593Smuzhiyun
bq24735_poll(struct work_struct * work)242*4882a593Smuzhiyun static void bq24735_poll(struct work_struct *work)
243*4882a593Smuzhiyun {
244*4882a593Smuzhiyun struct bq24735 *charger = container_of(work, struct bq24735, poll.work);
245*4882a593Smuzhiyun
246*4882a593Smuzhiyun bq24735_update(charger);
247*4882a593Smuzhiyun
248*4882a593Smuzhiyun schedule_delayed_work(&charger->poll,
249*4882a593Smuzhiyun msecs_to_jiffies(charger->poll_interval));
250*4882a593Smuzhiyun }
251*4882a593Smuzhiyun
bq24735_charger_get_property(struct power_supply * psy,enum power_supply_property psp,union power_supply_propval * val)252*4882a593Smuzhiyun static int bq24735_charger_get_property(struct power_supply *psy,
253*4882a593Smuzhiyun enum power_supply_property psp,
254*4882a593Smuzhiyun union power_supply_propval *val)
255*4882a593Smuzhiyun {
256*4882a593Smuzhiyun struct bq24735 *charger = to_bq24735(psy);
257*4882a593Smuzhiyun
258*4882a593Smuzhiyun switch (psp) {
259*4882a593Smuzhiyun case POWER_SUPPLY_PROP_ONLINE:
260*4882a593Smuzhiyun val->intval = bq24735_charger_is_present(charger) ? 1 : 0;
261*4882a593Smuzhiyun break;
262*4882a593Smuzhiyun case POWER_SUPPLY_PROP_STATUS:
263*4882a593Smuzhiyun switch (bq24735_charger_is_charging(charger)) {
264*4882a593Smuzhiyun case 1:
265*4882a593Smuzhiyun val->intval = POWER_SUPPLY_STATUS_CHARGING;
266*4882a593Smuzhiyun break;
267*4882a593Smuzhiyun case 0:
268*4882a593Smuzhiyun val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
269*4882a593Smuzhiyun break;
270*4882a593Smuzhiyun default:
271*4882a593Smuzhiyun val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
272*4882a593Smuzhiyun break;
273*4882a593Smuzhiyun }
274*4882a593Smuzhiyun break;
275*4882a593Smuzhiyun default:
276*4882a593Smuzhiyun return -EINVAL;
277*4882a593Smuzhiyun }
278*4882a593Smuzhiyun
279*4882a593Smuzhiyun return 0;
280*4882a593Smuzhiyun }
281*4882a593Smuzhiyun
bq24735_charger_set_property(struct power_supply * psy,enum power_supply_property psp,const union power_supply_propval * val)282*4882a593Smuzhiyun static int bq24735_charger_set_property(struct power_supply *psy,
283*4882a593Smuzhiyun enum power_supply_property psp,
284*4882a593Smuzhiyun const union power_supply_propval *val)
285*4882a593Smuzhiyun {
286*4882a593Smuzhiyun struct bq24735 *charger = to_bq24735(psy);
287*4882a593Smuzhiyun int ret;
288*4882a593Smuzhiyun
289*4882a593Smuzhiyun switch (psp) {
290*4882a593Smuzhiyun case POWER_SUPPLY_PROP_STATUS:
291*4882a593Smuzhiyun switch (val->intval) {
292*4882a593Smuzhiyun case POWER_SUPPLY_STATUS_CHARGING:
293*4882a593Smuzhiyun mutex_lock(&charger->lock);
294*4882a593Smuzhiyun charger->charging = true;
295*4882a593Smuzhiyun ret = bq24735_enable_charging(charger);
296*4882a593Smuzhiyun mutex_unlock(&charger->lock);
297*4882a593Smuzhiyun if (ret)
298*4882a593Smuzhiyun return ret;
299*4882a593Smuzhiyun break;
300*4882a593Smuzhiyun case POWER_SUPPLY_STATUS_DISCHARGING:
301*4882a593Smuzhiyun case POWER_SUPPLY_STATUS_NOT_CHARGING:
302*4882a593Smuzhiyun mutex_lock(&charger->lock);
303*4882a593Smuzhiyun charger->charging = false;
304*4882a593Smuzhiyun ret = bq24735_disable_charging(charger);
305*4882a593Smuzhiyun mutex_unlock(&charger->lock);
306*4882a593Smuzhiyun if (ret)
307*4882a593Smuzhiyun return ret;
308*4882a593Smuzhiyun break;
309*4882a593Smuzhiyun default:
310*4882a593Smuzhiyun return -EINVAL;
311*4882a593Smuzhiyun }
312*4882a593Smuzhiyun power_supply_changed(psy);
313*4882a593Smuzhiyun break;
314*4882a593Smuzhiyun default:
315*4882a593Smuzhiyun return -EPERM;
316*4882a593Smuzhiyun }
317*4882a593Smuzhiyun
318*4882a593Smuzhiyun return 0;
319*4882a593Smuzhiyun }
320*4882a593Smuzhiyun
bq24735_parse_dt_data(struct i2c_client * client)321*4882a593Smuzhiyun static struct bq24735_platform *bq24735_parse_dt_data(struct i2c_client *client)
322*4882a593Smuzhiyun {
323*4882a593Smuzhiyun struct bq24735_platform *pdata;
324*4882a593Smuzhiyun struct device_node *np = client->dev.of_node;
325*4882a593Smuzhiyun u32 val;
326*4882a593Smuzhiyun int ret;
327*4882a593Smuzhiyun
328*4882a593Smuzhiyun pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
329*4882a593Smuzhiyun if (!pdata) {
330*4882a593Smuzhiyun dev_err(&client->dev,
331*4882a593Smuzhiyun "Memory alloc for bq24735 pdata failed\n");
332*4882a593Smuzhiyun return NULL;
333*4882a593Smuzhiyun }
334*4882a593Smuzhiyun
335*4882a593Smuzhiyun ret = of_property_read_u32(np, "ti,charge-current", &val);
336*4882a593Smuzhiyun if (!ret)
337*4882a593Smuzhiyun pdata->charge_current = val;
338*4882a593Smuzhiyun
339*4882a593Smuzhiyun ret = of_property_read_u32(np, "ti,charge-voltage", &val);
340*4882a593Smuzhiyun if (!ret)
341*4882a593Smuzhiyun pdata->charge_voltage = val;
342*4882a593Smuzhiyun
343*4882a593Smuzhiyun ret = of_property_read_u32(np, "ti,input-current", &val);
344*4882a593Smuzhiyun if (!ret)
345*4882a593Smuzhiyun pdata->input_current = val;
346*4882a593Smuzhiyun
347*4882a593Smuzhiyun pdata->ext_control = of_property_read_bool(np, "ti,external-control");
348*4882a593Smuzhiyun
349*4882a593Smuzhiyun return pdata;
350*4882a593Smuzhiyun }
351*4882a593Smuzhiyun
bq24735_charger_probe(struct i2c_client * client,const struct i2c_device_id * id)352*4882a593Smuzhiyun static int bq24735_charger_probe(struct i2c_client *client,
353*4882a593Smuzhiyun const struct i2c_device_id *id)
354*4882a593Smuzhiyun {
355*4882a593Smuzhiyun int ret;
356*4882a593Smuzhiyun struct bq24735 *charger;
357*4882a593Smuzhiyun struct power_supply_desc *supply_desc;
358*4882a593Smuzhiyun struct power_supply_config psy_cfg = {};
359*4882a593Smuzhiyun char *name;
360*4882a593Smuzhiyun
361*4882a593Smuzhiyun charger = devm_kzalloc(&client->dev, sizeof(*charger), GFP_KERNEL);
362*4882a593Smuzhiyun if (!charger)
363*4882a593Smuzhiyun return -ENOMEM;
364*4882a593Smuzhiyun
365*4882a593Smuzhiyun mutex_init(&charger->lock);
366*4882a593Smuzhiyun charger->charging = true;
367*4882a593Smuzhiyun charger->pdata = client->dev.platform_data;
368*4882a593Smuzhiyun
369*4882a593Smuzhiyun if (IS_ENABLED(CONFIG_OF) && !charger->pdata && client->dev.of_node)
370*4882a593Smuzhiyun charger->pdata = bq24735_parse_dt_data(client);
371*4882a593Smuzhiyun
372*4882a593Smuzhiyun if (!charger->pdata) {
373*4882a593Smuzhiyun dev_err(&client->dev, "no platform data provided\n");
374*4882a593Smuzhiyun return -EINVAL;
375*4882a593Smuzhiyun }
376*4882a593Smuzhiyun
377*4882a593Smuzhiyun name = (char *)charger->pdata->name;
378*4882a593Smuzhiyun if (!name) {
379*4882a593Smuzhiyun name = devm_kasprintf(&client->dev, GFP_KERNEL,
380*4882a593Smuzhiyun "bq24735@%s",
381*4882a593Smuzhiyun dev_name(&client->dev));
382*4882a593Smuzhiyun if (!name) {
383*4882a593Smuzhiyun dev_err(&client->dev, "Failed to alloc device name\n");
384*4882a593Smuzhiyun return -ENOMEM;
385*4882a593Smuzhiyun }
386*4882a593Smuzhiyun }
387*4882a593Smuzhiyun
388*4882a593Smuzhiyun charger->client = client;
389*4882a593Smuzhiyun
390*4882a593Smuzhiyun supply_desc = &charger->charger_desc;
391*4882a593Smuzhiyun
392*4882a593Smuzhiyun supply_desc->name = name;
393*4882a593Smuzhiyun supply_desc->type = POWER_SUPPLY_TYPE_MAINS;
394*4882a593Smuzhiyun supply_desc->properties = bq24735_charger_properties;
395*4882a593Smuzhiyun supply_desc->num_properties = ARRAY_SIZE(bq24735_charger_properties);
396*4882a593Smuzhiyun supply_desc->get_property = bq24735_charger_get_property;
397*4882a593Smuzhiyun supply_desc->set_property = bq24735_charger_set_property;
398*4882a593Smuzhiyun supply_desc->property_is_writeable =
399*4882a593Smuzhiyun bq24735_charger_property_is_writeable;
400*4882a593Smuzhiyun
401*4882a593Smuzhiyun psy_cfg.supplied_to = charger->pdata->supplied_to;
402*4882a593Smuzhiyun psy_cfg.num_supplicants = charger->pdata->num_supplicants;
403*4882a593Smuzhiyun psy_cfg.of_node = client->dev.of_node;
404*4882a593Smuzhiyun psy_cfg.drv_data = charger;
405*4882a593Smuzhiyun
406*4882a593Smuzhiyun i2c_set_clientdata(client, charger);
407*4882a593Smuzhiyun
408*4882a593Smuzhiyun charger->status_gpio = devm_gpiod_get_optional(&client->dev,
409*4882a593Smuzhiyun "ti,ac-detect",
410*4882a593Smuzhiyun GPIOD_IN);
411*4882a593Smuzhiyun if (IS_ERR(charger->status_gpio)) {
412*4882a593Smuzhiyun ret = PTR_ERR(charger->status_gpio);
413*4882a593Smuzhiyun dev_err(&client->dev, "Getting gpio failed: %d\n", ret);
414*4882a593Smuzhiyun return ret;
415*4882a593Smuzhiyun }
416*4882a593Smuzhiyun
417*4882a593Smuzhiyun if (bq24735_charger_is_present(charger)) {
418*4882a593Smuzhiyun ret = bq24735_read_word(client, BQ24735_MANUFACTURER_ID);
419*4882a593Smuzhiyun if (ret < 0) {
420*4882a593Smuzhiyun dev_err(&client->dev, "Failed to read manufacturer id : %d\n",
421*4882a593Smuzhiyun ret);
422*4882a593Smuzhiyun return ret;
423*4882a593Smuzhiyun } else if (ret != 0x0040) {
424*4882a593Smuzhiyun dev_err(&client->dev,
425*4882a593Smuzhiyun "manufacturer id mismatch. 0x0040 != 0x%04x\n", ret);
426*4882a593Smuzhiyun return -ENODEV;
427*4882a593Smuzhiyun }
428*4882a593Smuzhiyun
429*4882a593Smuzhiyun ret = bq24735_read_word(client, BQ24735_DEVICE_ID);
430*4882a593Smuzhiyun if (ret < 0) {
431*4882a593Smuzhiyun dev_err(&client->dev, "Failed to read device id : %d\n", ret);
432*4882a593Smuzhiyun return ret;
433*4882a593Smuzhiyun } else if (ret != 0x000B) {
434*4882a593Smuzhiyun dev_err(&client->dev,
435*4882a593Smuzhiyun "device id mismatch. 0x000b != 0x%04x\n", ret);
436*4882a593Smuzhiyun return -ENODEV;
437*4882a593Smuzhiyun }
438*4882a593Smuzhiyun
439*4882a593Smuzhiyun ret = bq24735_enable_charging(charger);
440*4882a593Smuzhiyun if (ret < 0) {
441*4882a593Smuzhiyun dev_err(&client->dev, "Failed to enable charging\n");
442*4882a593Smuzhiyun return ret;
443*4882a593Smuzhiyun }
444*4882a593Smuzhiyun }
445*4882a593Smuzhiyun
446*4882a593Smuzhiyun charger->charger = devm_power_supply_register(&client->dev, supply_desc,
447*4882a593Smuzhiyun &psy_cfg);
448*4882a593Smuzhiyun if (IS_ERR(charger->charger)) {
449*4882a593Smuzhiyun ret = PTR_ERR(charger->charger);
450*4882a593Smuzhiyun dev_err(&client->dev, "Failed to register power supply: %d\n",
451*4882a593Smuzhiyun ret);
452*4882a593Smuzhiyun return ret;
453*4882a593Smuzhiyun }
454*4882a593Smuzhiyun
455*4882a593Smuzhiyun if (client->irq) {
456*4882a593Smuzhiyun ret = devm_request_threaded_irq(&client->dev, client->irq,
457*4882a593Smuzhiyun NULL, bq24735_charger_isr,
458*4882a593Smuzhiyun IRQF_TRIGGER_RISING |
459*4882a593Smuzhiyun IRQF_TRIGGER_FALLING |
460*4882a593Smuzhiyun IRQF_ONESHOT,
461*4882a593Smuzhiyun supply_desc->name,
462*4882a593Smuzhiyun charger->charger);
463*4882a593Smuzhiyun if (ret) {
464*4882a593Smuzhiyun dev_err(&client->dev,
465*4882a593Smuzhiyun "Unable to register IRQ %d err %d\n",
466*4882a593Smuzhiyun client->irq, ret);
467*4882a593Smuzhiyun return ret;
468*4882a593Smuzhiyun }
469*4882a593Smuzhiyun } else {
470*4882a593Smuzhiyun ret = device_property_read_u32(&client->dev, "poll-interval",
471*4882a593Smuzhiyun &charger->poll_interval);
472*4882a593Smuzhiyun if (ret)
473*4882a593Smuzhiyun return 0;
474*4882a593Smuzhiyun if (!charger->poll_interval)
475*4882a593Smuzhiyun return 0;
476*4882a593Smuzhiyun
477*4882a593Smuzhiyun INIT_DELAYED_WORK(&charger->poll, bq24735_poll);
478*4882a593Smuzhiyun schedule_delayed_work(&charger->poll,
479*4882a593Smuzhiyun msecs_to_jiffies(charger->poll_interval));
480*4882a593Smuzhiyun }
481*4882a593Smuzhiyun
482*4882a593Smuzhiyun return 0;
483*4882a593Smuzhiyun }
484*4882a593Smuzhiyun
bq24735_charger_remove(struct i2c_client * client)485*4882a593Smuzhiyun static int bq24735_charger_remove(struct i2c_client *client)
486*4882a593Smuzhiyun {
487*4882a593Smuzhiyun struct bq24735 *charger = i2c_get_clientdata(client);
488*4882a593Smuzhiyun
489*4882a593Smuzhiyun if (charger->poll_interval)
490*4882a593Smuzhiyun cancel_delayed_work_sync(&charger->poll);
491*4882a593Smuzhiyun
492*4882a593Smuzhiyun return 0;
493*4882a593Smuzhiyun }
494*4882a593Smuzhiyun
495*4882a593Smuzhiyun static const struct i2c_device_id bq24735_charger_id[] = {
496*4882a593Smuzhiyun { "bq24735-charger", 0 },
497*4882a593Smuzhiyun {}
498*4882a593Smuzhiyun };
499*4882a593Smuzhiyun MODULE_DEVICE_TABLE(i2c, bq24735_charger_id);
500*4882a593Smuzhiyun
501*4882a593Smuzhiyun static const struct of_device_id bq24735_match_ids[] = {
502*4882a593Smuzhiyun { .compatible = "ti,bq24735", },
503*4882a593Smuzhiyun { /* end */ }
504*4882a593Smuzhiyun };
505*4882a593Smuzhiyun MODULE_DEVICE_TABLE(of, bq24735_match_ids);
506*4882a593Smuzhiyun
507*4882a593Smuzhiyun static struct i2c_driver bq24735_charger_driver = {
508*4882a593Smuzhiyun .driver = {
509*4882a593Smuzhiyun .name = "bq24735-charger",
510*4882a593Smuzhiyun .of_match_table = bq24735_match_ids,
511*4882a593Smuzhiyun },
512*4882a593Smuzhiyun .probe = bq24735_charger_probe,
513*4882a593Smuzhiyun .remove = bq24735_charger_remove,
514*4882a593Smuzhiyun .id_table = bq24735_charger_id,
515*4882a593Smuzhiyun };
516*4882a593Smuzhiyun
517*4882a593Smuzhiyun module_i2c_driver(bq24735_charger_driver);
518*4882a593Smuzhiyun
519*4882a593Smuzhiyun MODULE_DESCRIPTION("bq24735 battery charging driver");
520*4882a593Smuzhiyun MODULE_AUTHOR("Darbha Sriharsha <dsriharsha@nvidia.com>");
521*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
522