xref: /openbmc/linux/drivers/mfd/tps65010.c (revision bb3d5934)
187c13493SDavid Brownell /*
287c13493SDavid Brownell  * tps65010 - driver for tps6501x power management chips
387c13493SDavid Brownell  *
487c13493SDavid Brownell  * Copyright (C) 2004 Texas Instruments
587c13493SDavid Brownell  * Copyright (C) 2004-2005 David Brownell
687c13493SDavid Brownell  *
787c13493SDavid Brownell  * This program is free software; you can redistribute it and/or modify
887c13493SDavid Brownell  * it under the terms of the GNU General Public License as published by
987c13493SDavid Brownell  * the Free Software Foundation; either version 2 of the License, or
1087c13493SDavid Brownell  * (at your option) any later version.
1187c13493SDavid Brownell  *
1287c13493SDavid Brownell  * This program is distributed in the hope that it will be useful,
1387c13493SDavid Brownell  * but WITHOUT ANY WARRANTY; without even the implied warranty of
1487c13493SDavid Brownell  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1587c13493SDavid Brownell  * GNU General Public License for more details.
1687c13493SDavid Brownell  *
1787c13493SDavid Brownell  * You should have received a copy of the GNU General Public License
1887c13493SDavid Brownell  * along with this program; if not, write to the Free Software
1987c13493SDavid Brownell  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
2087c13493SDavid Brownell  */
2187c13493SDavid Brownell 
2287c13493SDavid Brownell #include <linux/kernel.h>
2387c13493SDavid Brownell #include <linux/module.h>
2487c13493SDavid Brownell #include <linux/init.h>
2587c13493SDavid Brownell #include <linux/slab.h>
2687c13493SDavid Brownell #include <linux/interrupt.h>
2787c13493SDavid Brownell #include <linux/i2c.h>
2887c13493SDavid Brownell #include <linux/delay.h>
2987c13493SDavid Brownell #include <linux/workqueue.h>
3087c13493SDavid Brownell #include <linux/debugfs.h>
3187c13493SDavid Brownell #include <linux/seq_file.h>
3287c13493SDavid Brownell #include <linux/mutex.h>
3387c13493SDavid Brownell #include <linux/platform_device.h>
3487c13493SDavid Brownell 
3587c13493SDavid Brownell #include <linux/i2c/tps65010.h>
3687c13493SDavid Brownell 
3777b22897SAxel Lin #include <linux/gpio.h>
3887c13493SDavid Brownell 
3987c13493SDavid Brownell 
4087c13493SDavid Brownell /*-------------------------------------------------------------------------*/
4187c13493SDavid Brownell 
4287c13493SDavid Brownell #define	DRIVER_VERSION	"2 May 2005"
4387c13493SDavid Brownell #define	DRIVER_NAME	(tps65010_driver.driver.name)
4487c13493SDavid Brownell 
4587c13493SDavid Brownell MODULE_DESCRIPTION("TPS6501x Power Management Driver");
4687c13493SDavid Brownell MODULE_LICENSE("GPL");
4787c13493SDavid Brownell 
4887c13493SDavid Brownell static struct i2c_driver tps65010_driver;
4987c13493SDavid Brownell 
5087c13493SDavid Brownell /*-------------------------------------------------------------------------*/
5187c13493SDavid Brownell 
5287c13493SDavid Brownell /* This driver handles a family of multipurpose chips, which incorporate
5387c13493SDavid Brownell  * voltage regulators, lithium ion/polymer battery charging, GPIOs, LEDs,
5487c13493SDavid Brownell  * and other features often needed in portable devices like cell phones
5587c13493SDavid Brownell  * or digital cameras.
5687c13493SDavid Brownell  *
5787c13493SDavid Brownell  * The tps65011 and tps65013 have different voltage settings compared
5887c13493SDavid Brownell  * to tps65010 and tps65012.  The tps65013 has a NO_CHG status/irq.
5987c13493SDavid Brownell  * All except tps65010 have "wait" mode, possibly defaulted so that
6087c13493SDavid Brownell  * battery-insert != device-on.
6187c13493SDavid Brownell  *
6287c13493SDavid Brownell  * We could distinguish between some models by checking VDCDC1.UVLO or
6387c13493SDavid Brownell  * other registers, unless they've been changed already after powerup
6487c13493SDavid Brownell  * as part of board setup by a bootloader.
6587c13493SDavid Brownell  */
6687c13493SDavid Brownell enum tps_model {
6787c13493SDavid Brownell 	TPS65010,
6887c13493SDavid Brownell 	TPS65011,
6987c13493SDavid Brownell 	TPS65012,
7087c13493SDavid Brownell 	TPS65013,
7187c13493SDavid Brownell };
7287c13493SDavid Brownell 
7387c13493SDavid Brownell struct tps65010 {
7487c13493SDavid Brownell 	struct i2c_client	*client;
7587c13493SDavid Brownell 	struct mutex		lock;
7687c13493SDavid Brownell 	struct delayed_work	work;
7787c13493SDavid Brownell 	struct dentry		*file;
7887c13493SDavid Brownell 	unsigned		charging:1;
7987c13493SDavid Brownell 	unsigned		por:1;
8087c13493SDavid Brownell 	unsigned		model:8;
8187c13493SDavid Brownell 	u16			vbus;
8287c13493SDavid Brownell 	unsigned long		flags;
8387c13493SDavid Brownell #define	FLAG_VBUS_CHANGED	0
8487c13493SDavid Brownell #define	FLAG_IRQ_ENABLE		1
8587c13493SDavid Brownell 
8687c13493SDavid Brownell 	/* copies of last register state */
8787c13493SDavid Brownell 	u8			chgstatus, regstatus, chgconf;
8887c13493SDavid Brownell 	u8			nmask1, nmask2;
8987c13493SDavid Brownell 
9087c13493SDavid Brownell 	u8			outmask;
9187c13493SDavid Brownell 	struct gpio_chip	chip;
9287c13493SDavid Brownell 	struct platform_device	*leds;
9387c13493SDavid Brownell };
9487c13493SDavid Brownell 
9587c13493SDavid Brownell #define	POWER_POLL_DELAY	msecs_to_jiffies(5000)
9687c13493SDavid Brownell 
9787c13493SDavid Brownell /*-------------------------------------------------------------------------*/
9887c13493SDavid Brownell 
9987c13493SDavid Brownell #if	defined(DEBUG) || defined(CONFIG_DEBUG_FS)
10087c13493SDavid Brownell 
10187c13493SDavid Brownell static void dbg_chgstat(char *buf, size_t len, u8 chgstatus)
10287c13493SDavid Brownell {
10387c13493SDavid Brownell 	snprintf(buf, len, "%02x%s%s%s%s%s%s%s%s\n",
10487c13493SDavid Brownell 		chgstatus,
10587c13493SDavid Brownell 		(chgstatus & TPS_CHG_USB) ? " USB" : "",
10687c13493SDavid Brownell 		(chgstatus & TPS_CHG_AC) ? " AC" : "",
10787c13493SDavid Brownell 		(chgstatus & TPS_CHG_THERM) ? " therm" : "",
10887c13493SDavid Brownell 		(chgstatus & TPS_CHG_TERM) ? " done" :
10987c13493SDavid Brownell 			((chgstatus & (TPS_CHG_USB|TPS_CHG_AC))
11087c13493SDavid Brownell 				? " (charging)" : ""),
11187c13493SDavid Brownell 		(chgstatus & TPS_CHG_TAPER_TMO) ? " taper_tmo" : "",
11287c13493SDavid Brownell 		(chgstatus & TPS_CHG_CHG_TMO) ? " charge_tmo" : "",
11387c13493SDavid Brownell 		(chgstatus & TPS_CHG_PRECHG_TMO) ? " prechg_tmo" : "",
11487c13493SDavid Brownell 		(chgstatus & TPS_CHG_TEMP_ERR) ? " temp_err" : "");
11587c13493SDavid Brownell }
11687c13493SDavid Brownell 
11787c13493SDavid Brownell static void dbg_regstat(char *buf, size_t len, u8 regstatus)
11887c13493SDavid Brownell {
11987c13493SDavid Brownell 	snprintf(buf, len, "%02x %s%s%s%s%s%s%s%s\n",
12087c13493SDavid Brownell 		regstatus,
12187c13493SDavid Brownell 		(regstatus & TPS_REG_ONOFF) ? "off" : "(on)",
12287c13493SDavid Brownell 		(regstatus & TPS_REG_COVER) ? " uncover" : "",
12387c13493SDavid Brownell 		(regstatus & TPS_REG_UVLO) ? " UVLO" : "",
12487c13493SDavid Brownell 		(regstatus & TPS_REG_NO_CHG) ? " NO_CHG" : "",
12587c13493SDavid Brownell 		(regstatus & TPS_REG_PG_LD02) ? " ld02_bad" : "",
12687c13493SDavid Brownell 		(regstatus & TPS_REG_PG_LD01) ? " ld01_bad" : "",
12787c13493SDavid Brownell 		(regstatus & TPS_REG_PG_MAIN) ? " main_bad" : "",
12887c13493SDavid Brownell 		(regstatus & TPS_REG_PG_CORE) ? " core_bad" : "");
12987c13493SDavid Brownell }
13087c13493SDavid Brownell 
13187c13493SDavid Brownell static void dbg_chgconf(int por, char *buf, size_t len, u8 chgconfig)
13287c13493SDavid Brownell {
13387c13493SDavid Brownell 	const char *hibit;
13487c13493SDavid Brownell 
13587c13493SDavid Brownell 	if (por)
13687c13493SDavid Brownell 		hibit = (chgconfig & TPS_CHARGE_POR)
13787c13493SDavid Brownell 				? "POR=69ms" : "POR=1sec";
13887c13493SDavid Brownell 	else
13987c13493SDavid Brownell 		hibit = (chgconfig & TPS65013_AUA) ? "AUA" : "";
14087c13493SDavid Brownell 
14187c13493SDavid Brownell 	snprintf(buf, len, "%02x %s%s%s AC=%d%% USB=%dmA %sCharge\n",
14287c13493SDavid Brownell 		chgconfig, hibit,
14387c13493SDavid Brownell 		(chgconfig & TPS_CHARGE_RESET) ? " reset" : "",
14487c13493SDavid Brownell 		(chgconfig & TPS_CHARGE_FAST) ? " fast" : "",
14587c13493SDavid Brownell 		({int p; switch ((chgconfig >> 3) & 3) {
14687c13493SDavid Brownell 		case 3:		p = 100; break;
14787c13493SDavid Brownell 		case 2:		p = 75; break;
14887c13493SDavid Brownell 		case 1:		p = 50; break;
14987c13493SDavid Brownell 		default:	p = 25; break;
15087c13493SDavid Brownell 		}; p; }),
15187c13493SDavid Brownell 		(chgconfig & TPS_VBUS_CHARGING)
15287c13493SDavid Brownell 			? ((chgconfig & TPS_VBUS_500MA) ? 500 : 100)
15387c13493SDavid Brownell 			: 0,
15487c13493SDavid Brownell 		(chgconfig & TPS_CHARGE_ENABLE) ? "" : "No");
15587c13493SDavid Brownell }
15687c13493SDavid Brownell 
15787c13493SDavid Brownell #endif
15887c13493SDavid Brownell 
15987c13493SDavid Brownell #ifdef	DEBUG
16087c13493SDavid Brownell 
16187c13493SDavid Brownell static void show_chgstatus(const char *label, u8 chgstatus)
16287c13493SDavid Brownell {
16387c13493SDavid Brownell 	char buf [100];
16487c13493SDavid Brownell 
16587c13493SDavid Brownell 	dbg_chgstat(buf, sizeof buf, chgstatus);
16687c13493SDavid Brownell 	pr_debug("%s: %s %s", DRIVER_NAME, label, buf);
16787c13493SDavid Brownell }
16887c13493SDavid Brownell 
16987c13493SDavid Brownell static void show_regstatus(const char *label, u8 regstatus)
17087c13493SDavid Brownell {
17187c13493SDavid Brownell 	char buf [100];
17287c13493SDavid Brownell 
17387c13493SDavid Brownell 	dbg_regstat(buf, sizeof buf, regstatus);
17487c13493SDavid Brownell 	pr_debug("%s: %s %s", DRIVER_NAME, label, buf);
17587c13493SDavid Brownell }
17687c13493SDavid Brownell 
17787c13493SDavid Brownell static void show_chgconfig(int por, const char *label, u8 chgconfig)
17887c13493SDavid Brownell {
17987c13493SDavid Brownell 	char buf [100];
18087c13493SDavid Brownell 
18187c13493SDavid Brownell 	dbg_chgconf(por, buf, sizeof buf, chgconfig);
18287c13493SDavid Brownell 	pr_debug("%s: %s %s", DRIVER_NAME, label, buf);
18387c13493SDavid Brownell }
18487c13493SDavid Brownell 
18587c13493SDavid Brownell #else
18687c13493SDavid Brownell 
18787c13493SDavid Brownell static inline void show_chgstatus(const char *label, u8 chgstatus) { }
18887c13493SDavid Brownell static inline void show_regstatus(const char *label, u8 chgstatus) { }
18987c13493SDavid Brownell static inline void show_chgconfig(int por, const char *label, u8 chgconfig) { }
19087c13493SDavid Brownell 
19187c13493SDavid Brownell #endif
19287c13493SDavid Brownell 
19387c13493SDavid Brownell #ifdef	CONFIG_DEBUG_FS
19487c13493SDavid Brownell 
19587c13493SDavid Brownell static int dbg_show(struct seq_file *s, void *_)
19687c13493SDavid Brownell {
19787c13493SDavid Brownell 	struct tps65010	*tps = s->private;
19887c13493SDavid Brownell 	u8		value, v2;
19987c13493SDavid Brownell 	unsigned	i;
20087c13493SDavid Brownell 	char		buf[100];
20187c13493SDavid Brownell 	const char	*chip;
20287c13493SDavid Brownell 
20387c13493SDavid Brownell 	switch (tps->model) {
20487c13493SDavid Brownell 	case TPS65010:	chip = "tps65010"; break;
20587c13493SDavid Brownell 	case TPS65011:	chip = "tps65011"; break;
20687c13493SDavid Brownell 	case TPS65012:	chip = "tps65012"; break;
20787c13493SDavid Brownell 	case TPS65013:	chip = "tps65013"; break;
20887c13493SDavid Brownell 	default:	chip = NULL; break;
20987c13493SDavid Brownell 	}
21087c13493SDavid Brownell 	seq_printf(s, "driver  %s\nversion %s\nchip    %s\n\n",
21187c13493SDavid Brownell 			DRIVER_NAME, DRIVER_VERSION, chip);
21287c13493SDavid Brownell 
21387c13493SDavid Brownell 	mutex_lock(&tps->lock);
21487c13493SDavid Brownell 
21587c13493SDavid Brownell 	/* FIXME how can we tell whether a battery is present?
21687c13493SDavid Brownell 	 * likely involves a charge gauging chip (like BQ26501).
21787c13493SDavid Brownell 	 */
21887c13493SDavid Brownell 
21987c13493SDavid Brownell 	seq_printf(s, "%scharging\n\n", tps->charging ? "" : "(not) ");
22087c13493SDavid Brownell 
22187c13493SDavid Brownell 
22287c13493SDavid Brownell 	/* registers for monitoring battery charging and status; note
22387c13493SDavid Brownell 	 * that reading chgstat and regstat may ack IRQs...
22487c13493SDavid Brownell 	 */
22587c13493SDavid Brownell 	value = i2c_smbus_read_byte_data(tps->client, TPS_CHGCONFIG);
22687c13493SDavid Brownell 	dbg_chgconf(tps->por, buf, sizeof buf, value);
22787c13493SDavid Brownell 	seq_printf(s, "chgconfig %s", buf);
22887c13493SDavid Brownell 
22987c13493SDavid Brownell 	value = i2c_smbus_read_byte_data(tps->client, TPS_CHGSTATUS);
23087c13493SDavid Brownell 	dbg_chgstat(buf, sizeof buf, value);
23187c13493SDavid Brownell 	seq_printf(s, "chgstat   %s", buf);
23287c13493SDavid Brownell 	value = i2c_smbus_read_byte_data(tps->client, TPS_MASK1);
23387c13493SDavid Brownell 	dbg_chgstat(buf, sizeof buf, value);
23487c13493SDavid Brownell 	seq_printf(s, "mask1     %s", buf);
23587c13493SDavid Brownell 	/* ignore ackint1 */
23687c13493SDavid Brownell 
23787c13493SDavid Brownell 	value = i2c_smbus_read_byte_data(tps->client, TPS_REGSTATUS);
23887c13493SDavid Brownell 	dbg_regstat(buf, sizeof buf, value);
23987c13493SDavid Brownell 	seq_printf(s, "regstat   %s", buf);
24087c13493SDavid Brownell 	value = i2c_smbus_read_byte_data(tps->client, TPS_MASK2);
24187c13493SDavid Brownell 	dbg_regstat(buf, sizeof buf, value);
24287c13493SDavid Brownell 	seq_printf(s, "mask2     %s\n", buf);
24387c13493SDavid Brownell 	/* ignore ackint2 */
24487c13493SDavid Brownell 
245bb3d5934SMark Brown 	queue_delayed_work(system_power_efficient_wq, &tps->work,
246bb3d5934SMark Brown 			   POWER_POLL_DELAY);
24787c13493SDavid Brownell 
24887c13493SDavid Brownell 	/* VMAIN voltage, enable lowpower, etc */
24987c13493SDavid Brownell 	value = i2c_smbus_read_byte_data(tps->client, TPS_VDCDC1);
25087c13493SDavid Brownell 	seq_printf(s, "vdcdc1    %02x\n", value);
25187c13493SDavid Brownell 
25287c13493SDavid Brownell 	/* VCORE voltage, vibrator on/off */
25387c13493SDavid Brownell 	value = i2c_smbus_read_byte_data(tps->client, TPS_VDCDC2);
25487c13493SDavid Brownell 	seq_printf(s, "vdcdc2    %02x\n", value);
25587c13493SDavid Brownell 
25687c13493SDavid Brownell 	/* both LD0s, and their lowpower behavior */
25787c13493SDavid Brownell 	value = i2c_smbus_read_byte_data(tps->client, TPS_VREGS1);
25887c13493SDavid Brownell 	seq_printf(s, "vregs1    %02x\n\n", value);
25987c13493SDavid Brownell 
26087c13493SDavid Brownell 
26187c13493SDavid Brownell 	/* LEDs and GPIOs */
26287c13493SDavid Brownell 	value = i2c_smbus_read_byte_data(tps->client, TPS_LED1_ON);
26387c13493SDavid Brownell 	v2 = i2c_smbus_read_byte_data(tps->client, TPS_LED1_PER);
26487c13493SDavid Brownell 	seq_printf(s, "led1 %s, on=%02x, per=%02x, %d/%d msec\n",
26587c13493SDavid Brownell 		(value & 0x80)
26687c13493SDavid Brownell 			? ((v2 & 0x80) ? "on" : "off")
26787c13493SDavid Brownell 			: ((v2 & 0x80) ? "blink" : "(nPG)"),
26887c13493SDavid Brownell 		value, v2,
26987c13493SDavid Brownell 		(value & 0x7f) * 10, (v2 & 0x7f) * 100);
27087c13493SDavid Brownell 
27187c13493SDavid Brownell 	value = i2c_smbus_read_byte_data(tps->client, TPS_LED2_ON);
27287c13493SDavid Brownell 	v2 = i2c_smbus_read_byte_data(tps->client, TPS_LED2_PER);
27387c13493SDavid Brownell 	seq_printf(s, "led2 %s, on=%02x, per=%02x, %d/%d msec\n",
27487c13493SDavid Brownell 		(value & 0x80)
27587c13493SDavid Brownell 			? ((v2 & 0x80) ? "on" : "off")
27687c13493SDavid Brownell 			: ((v2 & 0x80) ? "blink" : "off"),
27787c13493SDavid Brownell 		value, v2,
27887c13493SDavid Brownell 		(value & 0x7f) * 10, (v2 & 0x7f) * 100);
27987c13493SDavid Brownell 
28087c13493SDavid Brownell 	value = i2c_smbus_read_byte_data(tps->client, TPS_DEFGPIO);
28187c13493SDavid Brownell 	v2 = i2c_smbus_read_byte_data(tps->client, TPS_MASK3);
28287c13493SDavid Brownell 	seq_printf(s, "defgpio %02x mask3 %02x\n", value, v2);
28387c13493SDavid Brownell 
28487c13493SDavid Brownell 	for (i = 0; i < 4; i++) {
28587c13493SDavid Brownell 		if (value & (1 << (4 + i)))
28687c13493SDavid Brownell 			seq_printf(s, "  gpio%d-out %s\n", i + 1,
28787c13493SDavid Brownell 				(value & (1 << i)) ? "low" : "hi ");
28887c13493SDavid Brownell 		else
28987c13493SDavid Brownell 			seq_printf(s, "  gpio%d-in  %s %s %s\n", i + 1,
29087c13493SDavid Brownell 				(value & (1 << i)) ? "hi " : "low",
29187c13493SDavid Brownell 				(v2 & (1 << i)) ? "no-irq" : "irq",
29287c13493SDavid Brownell 				(v2 & (1 << (4 + i))) ? "rising" : "falling");
29387c13493SDavid Brownell 	}
29487c13493SDavid Brownell 
29587c13493SDavid Brownell 	mutex_unlock(&tps->lock);
29687c13493SDavid Brownell 	return 0;
29787c13493SDavid Brownell }
29887c13493SDavid Brownell 
29987c13493SDavid Brownell static int dbg_tps_open(struct inode *inode, struct file *file)
30087c13493SDavid Brownell {
30187c13493SDavid Brownell 	return single_open(file, dbg_show, inode->i_private);
30287c13493SDavid Brownell }
30387c13493SDavid Brownell 
30487c13493SDavid Brownell static const struct file_operations debug_fops = {
30587c13493SDavid Brownell 	.open		= dbg_tps_open,
30687c13493SDavid Brownell 	.read		= seq_read,
30787c13493SDavid Brownell 	.llseek		= seq_lseek,
30887c13493SDavid Brownell 	.release	= single_release,
30987c13493SDavid Brownell };
31087c13493SDavid Brownell 
31187c13493SDavid Brownell #define	DEBUG_FOPS	&debug_fops
31287c13493SDavid Brownell 
31387c13493SDavid Brownell #else
31487c13493SDavid Brownell #define	DEBUG_FOPS	NULL
31587c13493SDavid Brownell #endif
31687c13493SDavid Brownell 
31787c13493SDavid Brownell /*-------------------------------------------------------------------------*/
31887c13493SDavid Brownell 
31987c13493SDavid Brownell /* handle IRQS in a task context, so we can use I2C calls */
32087c13493SDavid Brownell static void tps65010_interrupt(struct tps65010 *tps)
32187c13493SDavid Brownell {
32287c13493SDavid Brownell 	u8 tmp = 0, mask, poll;
32387c13493SDavid Brownell 
32487c13493SDavid Brownell 	/* IRQs won't trigger for certain events, but we can get
32587c13493SDavid Brownell 	 * others by polling (normally, with external power applied).
32687c13493SDavid Brownell 	 */
32787c13493SDavid Brownell 	poll = 0;
32887c13493SDavid Brownell 
32987c13493SDavid Brownell 	/* regstatus irqs */
33087c13493SDavid Brownell 	if (tps->nmask2) {
33187c13493SDavid Brownell 		tmp = i2c_smbus_read_byte_data(tps->client, TPS_REGSTATUS);
33287c13493SDavid Brownell 		mask = tmp ^ tps->regstatus;
33387c13493SDavid Brownell 		tps->regstatus = tmp;
33487c13493SDavid Brownell 		mask &= tps->nmask2;
33587c13493SDavid Brownell 	} else
33687c13493SDavid Brownell 		mask = 0;
33787c13493SDavid Brownell 	if (mask) {
33887c13493SDavid Brownell 		tps->regstatus =  tmp;
33987c13493SDavid Brownell 		/* may need to shut something down ... */
34087c13493SDavid Brownell 
34187c13493SDavid Brownell 		/* "off" usually means deep sleep */
34287c13493SDavid Brownell 		if (tmp & TPS_REG_ONOFF) {
34387c13493SDavid Brownell 			pr_info("%s: power off button\n", DRIVER_NAME);
34487c13493SDavid Brownell #if 0
34587c13493SDavid Brownell 			/* REVISIT:  this might need its own workqueue
34687c13493SDavid Brownell 			 * plus tweaks including deadlock avoidance ...
34787c13493SDavid Brownell 			 * also needs to get error handling and probably
34887c13493SDavid Brownell 			 * an #ifdef CONFIG_HIBERNATION
34987c13493SDavid Brownell 			 */
35087c13493SDavid Brownell 			hibernate();
35187c13493SDavid Brownell #endif
35287c13493SDavid Brownell 			poll = 1;
35387c13493SDavid Brownell 		}
35487c13493SDavid Brownell 	}
35587c13493SDavid Brownell 
35687c13493SDavid Brownell 	/* chgstatus irqs */
35787c13493SDavid Brownell 	if (tps->nmask1) {
35887c13493SDavid Brownell 		tmp = i2c_smbus_read_byte_data(tps->client, TPS_CHGSTATUS);
35987c13493SDavid Brownell 		mask = tmp ^ tps->chgstatus;
36087c13493SDavid Brownell 		tps->chgstatus = tmp;
36187c13493SDavid Brownell 		mask &= tps->nmask1;
36287c13493SDavid Brownell 	} else
36387c13493SDavid Brownell 		mask = 0;
36487c13493SDavid Brownell 	if (mask) {
36587c13493SDavid Brownell 		unsigned	charging = 0;
36687c13493SDavid Brownell 
36787c13493SDavid Brownell 		show_chgstatus("chg/irq", tmp);
36887c13493SDavid Brownell 		if (tmp & (TPS_CHG_USB|TPS_CHG_AC))
36987c13493SDavid Brownell 			show_chgconfig(tps->por, "conf", tps->chgconf);
37087c13493SDavid Brownell 
37187c13493SDavid Brownell 		/* Unless it was turned off or disabled, we charge any
37287c13493SDavid Brownell 		 * battery whenever there's power available for it
37387c13493SDavid Brownell 		 * and the charger hasn't been disabled.
37487c13493SDavid Brownell 		 */
37587c13493SDavid Brownell 		if (!(tps->chgstatus & ~(TPS_CHG_USB|TPS_CHG_AC))
37687c13493SDavid Brownell 				&& (tps->chgstatus & (TPS_CHG_USB|TPS_CHG_AC))
37787c13493SDavid Brownell 				&& (tps->chgconf & TPS_CHARGE_ENABLE)
37887c13493SDavid Brownell 				) {
37987c13493SDavid Brownell 			if (tps->chgstatus & TPS_CHG_USB) {
38087c13493SDavid Brownell 				/* VBUS options are readonly until reconnect */
38187c13493SDavid Brownell 				if (mask & TPS_CHG_USB)
38287c13493SDavid Brownell 					set_bit(FLAG_VBUS_CHANGED, &tps->flags);
38387c13493SDavid Brownell 				charging = 1;
38487c13493SDavid Brownell 			} else if (tps->chgstatus & TPS_CHG_AC)
38587c13493SDavid Brownell 				charging = 1;
38687c13493SDavid Brownell 		}
38787c13493SDavid Brownell 		if (charging != tps->charging) {
38887c13493SDavid Brownell 			tps->charging = charging;
38987c13493SDavid Brownell 			pr_info("%s: battery %scharging\n",
39087c13493SDavid Brownell 				DRIVER_NAME, charging ? "" :
39187c13493SDavid Brownell 				((tps->chgstatus & (TPS_CHG_USB|TPS_CHG_AC))
39287c13493SDavid Brownell 					? "NOT " : "dis"));
39387c13493SDavid Brownell 		}
39487c13493SDavid Brownell 	}
39587c13493SDavid Brownell 
39687c13493SDavid Brownell 	/* always poll to detect (a) power removal, without tps65013
39787c13493SDavid Brownell 	 * NO_CHG IRQ; or (b) restart of charging after stop.
39887c13493SDavid Brownell 	 */
39987c13493SDavid Brownell 	if ((tps->model != TPS65013 || !tps->charging)
40087c13493SDavid Brownell 			&& (tps->chgstatus & (TPS_CHG_USB|TPS_CHG_AC)))
40187c13493SDavid Brownell 		poll = 1;
40287c13493SDavid Brownell 	if (poll)
403bb3d5934SMark Brown 		queue_delayed_work(system_power_efficient_wq, &tps->work,
404bb3d5934SMark Brown 				   POWER_POLL_DELAY);
40587c13493SDavid Brownell 
40687c13493SDavid Brownell 	/* also potentially gpio-in rise or fall */
40787c13493SDavid Brownell }
40887c13493SDavid Brownell 
40987c13493SDavid Brownell /* handle IRQs and polling using keventd for now */
41087c13493SDavid Brownell static void tps65010_work(struct work_struct *work)
41187c13493SDavid Brownell {
41287c13493SDavid Brownell 	struct tps65010		*tps;
41387c13493SDavid Brownell 
414afdb32f2STejun Heo 	tps = container_of(to_delayed_work(work), struct tps65010, work);
41587c13493SDavid Brownell 	mutex_lock(&tps->lock);
41687c13493SDavid Brownell 
41787c13493SDavid Brownell 	tps65010_interrupt(tps);
41887c13493SDavid Brownell 
41987c13493SDavid Brownell 	if (test_and_clear_bit(FLAG_VBUS_CHANGED, &tps->flags)) {
42087c13493SDavid Brownell 		int	status;
42187c13493SDavid Brownell 		u8	chgconfig, tmp;
42287c13493SDavid Brownell 
42387c13493SDavid Brownell 		chgconfig = i2c_smbus_read_byte_data(tps->client,
42487c13493SDavid Brownell 					TPS_CHGCONFIG);
42587c13493SDavid Brownell 		chgconfig &= ~(TPS_VBUS_500MA | TPS_VBUS_CHARGING);
42687c13493SDavid Brownell 		if (tps->vbus == 500)
42787c13493SDavid Brownell 			chgconfig |= TPS_VBUS_500MA | TPS_VBUS_CHARGING;
42887c13493SDavid Brownell 		else if (tps->vbus >= 100)
42987c13493SDavid Brownell 			chgconfig |= TPS_VBUS_CHARGING;
43087c13493SDavid Brownell 
43187c13493SDavid Brownell 		status = i2c_smbus_write_byte_data(tps->client,
43287c13493SDavid Brownell 				TPS_CHGCONFIG, chgconfig);
43387c13493SDavid Brownell 
43487c13493SDavid Brownell 		/* vbus update fails unless VBUS is connected! */
43587c13493SDavid Brownell 		tmp = i2c_smbus_read_byte_data(tps->client, TPS_CHGCONFIG);
43687c13493SDavid Brownell 		tps->chgconf = tmp;
43787c13493SDavid Brownell 		show_chgconfig(tps->por, "update vbus", tmp);
43887c13493SDavid Brownell 	}
43987c13493SDavid Brownell 
44087c13493SDavid Brownell 	if (test_and_clear_bit(FLAG_IRQ_ENABLE, &tps->flags))
44187c13493SDavid Brownell 		enable_irq(tps->client->irq);
44287c13493SDavid Brownell 
44387c13493SDavid Brownell 	mutex_unlock(&tps->lock);
44487c13493SDavid Brownell }
44587c13493SDavid Brownell 
44687c13493SDavid Brownell static irqreturn_t tps65010_irq(int irq, void *_tps)
44787c13493SDavid Brownell {
44887c13493SDavid Brownell 	struct tps65010		*tps = _tps;
44987c13493SDavid Brownell 
45087c13493SDavid Brownell 	disable_irq_nosync(irq);
45187c13493SDavid Brownell 	set_bit(FLAG_IRQ_ENABLE, &tps->flags);
452bb3d5934SMark Brown 	queue_delayed_work(system_power_efficient_wq, &tps->work, 0);
45387c13493SDavid Brownell 	return IRQ_HANDLED;
45487c13493SDavid Brownell }
45587c13493SDavid Brownell 
45687c13493SDavid Brownell /*-------------------------------------------------------------------------*/
45787c13493SDavid Brownell 
45887c13493SDavid Brownell /* offsets 0..3 == GPIO1..GPIO4
45987c13493SDavid Brownell  * offsets 4..5 == LED1/nPG, LED2 (we set one of the non-BLINK modes)
46087c13493SDavid Brownell  * offset 6 == vibrator motor driver
46187c13493SDavid Brownell  */
46287c13493SDavid Brownell static void
46387c13493SDavid Brownell tps65010_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
46487c13493SDavid Brownell {
46587c13493SDavid Brownell 	if (offset < 4)
46687c13493SDavid Brownell 		tps65010_set_gpio_out_value(offset + 1, value);
46787c13493SDavid Brownell 	else if (offset < 6)
46887c13493SDavid Brownell 		tps65010_set_led(offset - 3, value ? ON : OFF);
46987c13493SDavid Brownell 	else
47087c13493SDavid Brownell 		tps65010_set_vib(value);
47187c13493SDavid Brownell }
47287c13493SDavid Brownell 
47387c13493SDavid Brownell static int
47487c13493SDavid Brownell tps65010_output(struct gpio_chip *chip, unsigned offset, int value)
47587c13493SDavid Brownell {
47687c13493SDavid Brownell 	/* GPIOs may be input-only */
47787c13493SDavid Brownell 	if (offset < 4) {
47887c13493SDavid Brownell 		struct tps65010		*tps;
47987c13493SDavid Brownell 
48087c13493SDavid Brownell 		tps = container_of(chip, struct tps65010, chip);
48187c13493SDavid Brownell 		if (!(tps->outmask & (1 << offset)))
48287c13493SDavid Brownell 			return -EINVAL;
48387c13493SDavid Brownell 		tps65010_set_gpio_out_value(offset + 1, value);
48487c13493SDavid Brownell 	} else if (offset < 6)
48587c13493SDavid Brownell 		tps65010_set_led(offset - 3, value ? ON : OFF);
48687c13493SDavid Brownell 	else
48787c13493SDavid Brownell 		tps65010_set_vib(value);
48887c13493SDavid Brownell 
48987c13493SDavid Brownell 	return 0;
49087c13493SDavid Brownell }
49187c13493SDavid Brownell 
49287c13493SDavid Brownell static int tps65010_gpio_get(struct gpio_chip *chip, unsigned offset)
49387c13493SDavid Brownell {
49487c13493SDavid Brownell 	int			value;
49587c13493SDavid Brownell 	struct tps65010		*tps;
49687c13493SDavid Brownell 
49787c13493SDavid Brownell 	tps = container_of(chip, struct tps65010, chip);
49887c13493SDavid Brownell 
49987c13493SDavid Brownell 	if (offset < 4) {
50087c13493SDavid Brownell 		value = i2c_smbus_read_byte_data(tps->client, TPS_DEFGPIO);
50187c13493SDavid Brownell 		if (value < 0)
50287c13493SDavid Brownell 			return 0;
50387c13493SDavid Brownell 		if (value & (1 << (offset + 4)))	/* output */
50487c13493SDavid Brownell 			return !(value & (1 << offset));
50587c13493SDavid Brownell 		else					/* input */
50687c13493SDavid Brownell 			return (value & (1 << offset));
50787c13493SDavid Brownell 	}
50887c13493SDavid Brownell 
50987c13493SDavid Brownell 	/* REVISIT we *could* report LED1/nPG and LED2 state ... */
51087c13493SDavid Brownell 	return 0;
51187c13493SDavid Brownell }
51287c13493SDavid Brownell 
51387c13493SDavid Brownell 
51487c13493SDavid Brownell /*-------------------------------------------------------------------------*/
51587c13493SDavid Brownell 
51687c13493SDavid Brownell static struct tps65010 *the_tps;
51787c13493SDavid Brownell 
51887c13493SDavid Brownell static int __exit tps65010_remove(struct i2c_client *client)
51987c13493SDavid Brownell {
52087c13493SDavid Brownell 	struct tps65010		*tps = i2c_get_clientdata(client);
521334a41ceSJingoo Han 	struct tps65010_board	*board = dev_get_platdata(&client->dev);
52287c13493SDavid Brownell 
52387c13493SDavid Brownell 	if (board && board->teardown) {
52487c13493SDavid Brownell 		int status = board->teardown(client, board->context);
52587c13493SDavid Brownell 		if (status < 0)
52687c13493SDavid Brownell 			dev_dbg(&client->dev, "board %s %s err %d\n",
52787c13493SDavid Brownell 				"teardown", client->name, status);
52887c13493SDavid Brownell 	}
52987c13493SDavid Brownell 	if (client->irq > 0)
53087c13493SDavid Brownell 		free_irq(client->irq, tps);
531afdb32f2STejun Heo 	cancel_delayed_work_sync(&tps->work);
53287c13493SDavid Brownell 	debugfs_remove(tps->file);
533f322d5f0SWolfram Sang 	kfree(tps);
53487c13493SDavid Brownell 	the_tps = NULL;
53587c13493SDavid Brownell 	return 0;
53687c13493SDavid Brownell }
53787c13493SDavid Brownell 
53887c13493SDavid Brownell static int tps65010_probe(struct i2c_client *client,
53987c13493SDavid Brownell 			  const struct i2c_device_id *id)
54087c13493SDavid Brownell {
54187c13493SDavid Brownell 	struct tps65010		*tps;
54287c13493SDavid Brownell 	int			status;
543334a41ceSJingoo Han 	struct tps65010_board	*board = dev_get_platdata(&client->dev);
54487c13493SDavid Brownell 
54587c13493SDavid Brownell 	if (the_tps) {
54687c13493SDavid Brownell 		dev_dbg(&client->dev, "only one tps6501x chip allowed\n");
54787c13493SDavid Brownell 		return -ENODEV;
54887c13493SDavid Brownell 	}
54987c13493SDavid Brownell 
55087c13493SDavid Brownell 	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
55187c13493SDavid Brownell 		return -EINVAL;
55287c13493SDavid Brownell 
55387c13493SDavid Brownell 	tps = kzalloc(sizeof *tps, GFP_KERNEL);
55487c13493SDavid Brownell 	if (!tps)
55587c13493SDavid Brownell 		return -ENOMEM;
55687c13493SDavid Brownell 
55787c13493SDavid Brownell 	mutex_init(&tps->lock);
55887c13493SDavid Brownell 	INIT_DELAYED_WORK(&tps->work, tps65010_work);
55987c13493SDavid Brownell 	tps->client = client;
56087c13493SDavid Brownell 	tps->model = id->driver_data;
56187c13493SDavid Brownell 
56287c13493SDavid Brownell 	/* the IRQ is active low, but many gpio lines can't support that
56387c13493SDavid Brownell 	 * so this driver uses falling-edge triggers instead.
56487c13493SDavid Brownell 	 */
56587c13493SDavid Brownell 	if (client->irq > 0) {
56687c13493SDavid Brownell 		status = request_irq(client->irq, tps65010_irq,
567212436c2STheodore Ts'o 				     IRQF_TRIGGER_FALLING, DRIVER_NAME, tps);
56887c13493SDavid Brownell 		if (status < 0) {
56987c13493SDavid Brownell 			dev_dbg(&client->dev, "can't get IRQ %d, err %d\n",
57087c13493SDavid Brownell 					client->irq, status);
57187c13493SDavid Brownell 			goto fail1;
57287c13493SDavid Brownell 		}
57387c13493SDavid Brownell 		/* annoying race here, ideally we'd have an option
57487c13493SDavid Brownell 		 * to claim the irq now and enable it later.
57587c13493SDavid Brownell 		 * FIXME genirq IRQF_NOAUTOEN now solves that ...
57687c13493SDavid Brownell 		 */
57787c13493SDavid Brownell 		disable_irq(client->irq);
57887c13493SDavid Brownell 		set_bit(FLAG_IRQ_ENABLE, &tps->flags);
57987c13493SDavid Brownell 	} else
58087c13493SDavid Brownell 		dev_warn(&client->dev, "IRQ not configured!\n");
58187c13493SDavid Brownell 
58287c13493SDavid Brownell 
58387c13493SDavid Brownell 	switch (tps->model) {
58487c13493SDavid Brownell 	case TPS65010:
58587c13493SDavid Brownell 	case TPS65012:
58687c13493SDavid Brownell 		tps->por = 1;
58787c13493SDavid Brownell 		break;
58887c13493SDavid Brownell 	/* else CHGCONFIG.POR is replaced by AUA, enabling a WAIT mode */
58987c13493SDavid Brownell 	}
59087c13493SDavid Brownell 	tps->chgconf = i2c_smbus_read_byte_data(client, TPS_CHGCONFIG);
59187c13493SDavid Brownell 	show_chgconfig(tps->por, "conf/init", tps->chgconf);
59287c13493SDavid Brownell 
59387c13493SDavid Brownell 	show_chgstatus("chg/init",
59487c13493SDavid Brownell 		i2c_smbus_read_byte_data(client, TPS_CHGSTATUS));
59587c13493SDavid Brownell 	show_regstatus("reg/init",
59687c13493SDavid Brownell 		i2c_smbus_read_byte_data(client, TPS_REGSTATUS));
59787c13493SDavid Brownell 
59887c13493SDavid Brownell 	pr_debug("%s: vdcdc1 0x%02x, vdcdc2 %02x, vregs1 %02x\n", DRIVER_NAME,
59987c13493SDavid Brownell 		i2c_smbus_read_byte_data(client, TPS_VDCDC1),
60087c13493SDavid Brownell 		i2c_smbus_read_byte_data(client, TPS_VDCDC2),
60187c13493SDavid Brownell 		i2c_smbus_read_byte_data(client, TPS_VREGS1));
60287c13493SDavid Brownell 	pr_debug("%s: defgpio 0x%02x, mask3 0x%02x\n", DRIVER_NAME,
60387c13493SDavid Brownell 		i2c_smbus_read_byte_data(client, TPS_DEFGPIO),
60487c13493SDavid Brownell 		i2c_smbus_read_byte_data(client, TPS_MASK3));
60587c13493SDavid Brownell 
60687c13493SDavid Brownell 	i2c_set_clientdata(client, tps);
60787c13493SDavid Brownell 	the_tps = tps;
60887c13493SDavid Brownell 
60987c13493SDavid Brownell #if	defined(CONFIG_USB_GADGET) && !defined(CONFIG_USB_OTG)
61087c13493SDavid Brownell 	/* USB hosts can't draw VBUS.  OTG devices could, later
61187c13493SDavid Brownell 	 * when OTG infrastructure enables it.  USB peripherals
61287c13493SDavid Brownell 	 * could be relying on VBUS while booting, though.
61387c13493SDavid Brownell 	 */
61487c13493SDavid Brownell 	tps->vbus = 100;
61587c13493SDavid Brownell #endif
61687c13493SDavid Brownell 
61787c13493SDavid Brownell 	/* unmask the "interesting" irqs, then poll once to
61887c13493SDavid Brownell 	 * kickstart monitoring, initialize shadowed status
61987c13493SDavid Brownell 	 * registers, and maybe disable VBUS draw.
62087c13493SDavid Brownell 	 */
62187c13493SDavid Brownell 	tps->nmask1 = ~0;
62287c13493SDavid Brownell 	(void) i2c_smbus_write_byte_data(client, TPS_MASK1, ~tps->nmask1);
62387c13493SDavid Brownell 
62487c13493SDavid Brownell 	tps->nmask2 = TPS_REG_ONOFF;
62587c13493SDavid Brownell 	if (tps->model == TPS65013)
62687c13493SDavid Brownell 		tps->nmask2 |= TPS_REG_NO_CHG;
62787c13493SDavid Brownell 	(void) i2c_smbus_write_byte_data(client, TPS_MASK2, ~tps->nmask2);
62887c13493SDavid Brownell 
62987c13493SDavid Brownell 	(void) i2c_smbus_write_byte_data(client, TPS_MASK3, 0x0f
63087c13493SDavid Brownell 		| i2c_smbus_read_byte_data(client, TPS_MASK3));
63187c13493SDavid Brownell 
63287c13493SDavid Brownell 	tps65010_work(&tps->work.work);
63387c13493SDavid Brownell 
63487c13493SDavid Brownell 	tps->file = debugfs_create_file(DRIVER_NAME, S_IRUGO, NULL,
63587c13493SDavid Brownell 				tps, DEBUG_FOPS);
63687c13493SDavid Brownell 
63787c13493SDavid Brownell 	/* optionally register GPIOs */
63884836992SBen Dooks 	if (board && board->base != 0) {
63987c13493SDavid Brownell 		tps->outmask = board->outmask;
64087c13493SDavid Brownell 
64187c13493SDavid Brownell 		tps->chip.label = client->name;
64287c13493SDavid Brownell 		tps->chip.dev = &client->dev;
64387c13493SDavid Brownell 		tps->chip.owner = THIS_MODULE;
64487c13493SDavid Brownell 
64587c13493SDavid Brownell 		tps->chip.set = tps65010_gpio_set;
64687c13493SDavid Brownell 		tps->chip.direction_output = tps65010_output;
64787c13493SDavid Brownell 
64887c13493SDavid Brownell 		/* NOTE:  only partial support for inputs; nyet IRQs */
64987c13493SDavid Brownell 		tps->chip.get = tps65010_gpio_get;
65087c13493SDavid Brownell 
65187c13493SDavid Brownell 		tps->chip.base = board->base;
65287c13493SDavid Brownell 		tps->chip.ngpio = 7;
65387c13493SDavid Brownell 		tps->chip.can_sleep = 1;
65487c13493SDavid Brownell 
65587c13493SDavid Brownell 		status = gpiochip_add(&tps->chip);
65687c13493SDavid Brownell 		if (status < 0)
65787c13493SDavid Brownell 			dev_err(&client->dev, "can't add gpiochip, err %d\n",
65887c13493SDavid Brownell 					status);
65987c13493SDavid Brownell 		else if (board->setup) {
66087c13493SDavid Brownell 			status = board->setup(client, board->context);
66187c13493SDavid Brownell 			if (status < 0) {
66287c13493SDavid Brownell 				dev_dbg(&client->dev,
66387c13493SDavid Brownell 					"board %s %s err %d\n",
66487c13493SDavid Brownell 					"setup", client->name, status);
66587c13493SDavid Brownell 				status = 0;
66687c13493SDavid Brownell 			}
66787c13493SDavid Brownell 		}
66887c13493SDavid Brownell 	}
66987c13493SDavid Brownell 
67087c13493SDavid Brownell 	return 0;
67187c13493SDavid Brownell fail1:
67287c13493SDavid Brownell 	kfree(tps);
67387c13493SDavid Brownell 	return status;
67487c13493SDavid Brownell }
67587c13493SDavid Brownell 
67687c13493SDavid Brownell static const struct i2c_device_id tps65010_id[] = {
67787c13493SDavid Brownell 	{ "tps65010", TPS65010 },
67887c13493SDavid Brownell 	{ "tps65011", TPS65011 },
67987c13493SDavid Brownell 	{ "tps65012", TPS65012 },
68087c13493SDavid Brownell 	{ "tps65013", TPS65013 },
68187c13493SDavid Brownell 	{ "tps65014", TPS65011 },	/* tps65011 charging at 6.5V max */
68287c13493SDavid Brownell 	{ }
68387c13493SDavid Brownell };
68487c13493SDavid Brownell MODULE_DEVICE_TABLE(i2c, tps65010_id);
68587c13493SDavid Brownell 
68687c13493SDavid Brownell static struct i2c_driver tps65010_driver = {
68787c13493SDavid Brownell 	.driver = {
68887c13493SDavid Brownell 		.name	= "tps65010",
68987c13493SDavid Brownell 	},
69087c13493SDavid Brownell 	.probe	= tps65010_probe,
69187c13493SDavid Brownell 	.remove	= __exit_p(tps65010_remove),
69287c13493SDavid Brownell 	.id_table = tps65010_id,
69387c13493SDavid Brownell };
69487c13493SDavid Brownell 
69587c13493SDavid Brownell /*-------------------------------------------------------------------------*/
69687c13493SDavid Brownell 
69787c13493SDavid Brownell /* Draw from VBUS:
69887c13493SDavid Brownell  *   0 mA -- DON'T DRAW (might supply power instead)
69987c13493SDavid Brownell  * 100 mA -- usb unit load (slowest charge rate)
70087c13493SDavid Brownell  * 500 mA -- usb high power (fast battery charge)
70187c13493SDavid Brownell  */
70287c13493SDavid Brownell int tps65010_set_vbus_draw(unsigned mA)
70387c13493SDavid Brownell {
70487c13493SDavid Brownell 	unsigned long	flags;
70587c13493SDavid Brownell 
70687c13493SDavid Brownell 	if (!the_tps)
70787c13493SDavid Brownell 		return -ENODEV;
70887c13493SDavid Brownell 
70987c13493SDavid Brownell 	/* assumes non-SMP */
71087c13493SDavid Brownell 	local_irq_save(flags);
71187c13493SDavid Brownell 	if (mA >= 500)
71287c13493SDavid Brownell 		mA = 500;
71387c13493SDavid Brownell 	else if (mA >= 100)
71487c13493SDavid Brownell 		mA = 100;
71587c13493SDavid Brownell 	else
71687c13493SDavid Brownell 		mA = 0;
71787c13493SDavid Brownell 	the_tps->vbus = mA;
71887c13493SDavid Brownell 	if ((the_tps->chgstatus & TPS_CHG_USB)
71987c13493SDavid Brownell 			&& test_and_set_bit(
72087c13493SDavid Brownell 				FLAG_VBUS_CHANGED, &the_tps->flags)) {
72187c13493SDavid Brownell 		/* gadget drivers call this in_irq() */
722bb3d5934SMark Brown 		queue_delayed_work(system_power_efficient_wq, &the_tps->work,
723bb3d5934SMark Brown 				   0);
72487c13493SDavid Brownell 	}
72587c13493SDavid Brownell 	local_irq_restore(flags);
72687c13493SDavid Brownell 
72787c13493SDavid Brownell 	return 0;
72887c13493SDavid Brownell }
72987c13493SDavid Brownell EXPORT_SYMBOL(tps65010_set_vbus_draw);
73087c13493SDavid Brownell 
73187c13493SDavid Brownell /*-------------------------------------------------------------------------*/
73287c13493SDavid Brownell /* tps65010_set_gpio_out_value parameter:
73387c13493SDavid Brownell  * gpio:  GPIO1, GPIO2, GPIO3 or GPIO4
73487c13493SDavid Brownell  * value: LOW or HIGH
73587c13493SDavid Brownell  */
73687c13493SDavid Brownell int tps65010_set_gpio_out_value(unsigned gpio, unsigned value)
73787c13493SDavid Brownell {
73887c13493SDavid Brownell 	int	 status;
73987c13493SDavid Brownell 	unsigned defgpio;
74087c13493SDavid Brownell 
74187c13493SDavid Brownell 	if (!the_tps)
74287c13493SDavid Brownell 		return -ENODEV;
74387c13493SDavid Brownell 	if ((gpio < GPIO1) || (gpio > GPIO4))
74487c13493SDavid Brownell 		return -EINVAL;
74587c13493SDavid Brownell 
74687c13493SDavid Brownell 	mutex_lock(&the_tps->lock);
74787c13493SDavid Brownell 
74887c13493SDavid Brownell 	defgpio = i2c_smbus_read_byte_data(the_tps->client, TPS_DEFGPIO);
74987c13493SDavid Brownell 
75087c13493SDavid Brownell 	/* Configure GPIO for output */
75187c13493SDavid Brownell 	defgpio |= 1 << (gpio + 3);
75287c13493SDavid Brownell 
75387c13493SDavid Brownell 	/* Writing 1 forces a logic 0 on that GPIO and vice versa */
75487c13493SDavid Brownell 	switch (value) {
75587c13493SDavid Brownell 	case LOW:
75687c13493SDavid Brownell 		defgpio |= 1 << (gpio - 1);    /* set GPIO low by writing 1 */
75787c13493SDavid Brownell 		break;
75887c13493SDavid Brownell 	/* case HIGH: */
75987c13493SDavid Brownell 	default:
76087c13493SDavid Brownell 		defgpio &= ~(1 << (gpio - 1)); /* set GPIO high by writing 0 */
76187c13493SDavid Brownell 		break;
76287c13493SDavid Brownell 	}
76387c13493SDavid Brownell 
76487c13493SDavid Brownell 	status = i2c_smbus_write_byte_data(the_tps->client,
76587c13493SDavid Brownell 		TPS_DEFGPIO, defgpio);
76687c13493SDavid Brownell 
76787c13493SDavid Brownell 	pr_debug("%s: gpio%dout = %s, defgpio 0x%02x\n", DRIVER_NAME,
76887c13493SDavid Brownell 		gpio, value ? "high" : "low",
76987c13493SDavid Brownell 		i2c_smbus_read_byte_data(the_tps->client, TPS_DEFGPIO));
77087c13493SDavid Brownell 
77187c13493SDavid Brownell 	mutex_unlock(&the_tps->lock);
77287c13493SDavid Brownell 	return status;
77387c13493SDavid Brownell }
77487c13493SDavid Brownell EXPORT_SYMBOL(tps65010_set_gpio_out_value);
77587c13493SDavid Brownell 
77687c13493SDavid Brownell /*-------------------------------------------------------------------------*/
77787c13493SDavid Brownell /* tps65010_set_led parameter:
77887c13493SDavid Brownell  * led:  LED1 or LED2
77987c13493SDavid Brownell  * mode: ON, OFF or BLINK
78087c13493SDavid Brownell  */
78187c13493SDavid Brownell int tps65010_set_led(unsigned led, unsigned mode)
78287c13493SDavid Brownell {
78387c13493SDavid Brownell 	int	 status;
78487c13493SDavid Brownell 	unsigned led_on, led_per, offs;
78587c13493SDavid Brownell 
78687c13493SDavid Brownell 	if (!the_tps)
78787c13493SDavid Brownell 		return -ENODEV;
78887c13493SDavid Brownell 
78987c13493SDavid Brownell 	if (led == LED1)
79087c13493SDavid Brownell 		offs = 0;
79187c13493SDavid Brownell 	else {
79287c13493SDavid Brownell 		offs = 2;
79387c13493SDavid Brownell 		led = LED2;
79487c13493SDavid Brownell 	}
79587c13493SDavid Brownell 
79687c13493SDavid Brownell 	mutex_lock(&the_tps->lock);
79787c13493SDavid Brownell 
79887c13493SDavid Brownell 	pr_debug("%s: led%i_on   0x%02x\n", DRIVER_NAME, led,
79987c13493SDavid Brownell 		i2c_smbus_read_byte_data(the_tps->client,
80087c13493SDavid Brownell 				TPS_LED1_ON + offs));
80187c13493SDavid Brownell 
80287c13493SDavid Brownell 	pr_debug("%s: led%i_per  0x%02x\n", DRIVER_NAME, led,
80387c13493SDavid Brownell 		i2c_smbus_read_byte_data(the_tps->client,
80487c13493SDavid Brownell 				TPS_LED1_PER + offs));
80587c13493SDavid Brownell 
80687c13493SDavid Brownell 	switch (mode) {
80787c13493SDavid Brownell 	case OFF:
80887c13493SDavid Brownell 		led_on  = 1 << 7;
80987c13493SDavid Brownell 		led_per = 0 << 7;
81087c13493SDavid Brownell 		break;
81187c13493SDavid Brownell 	case ON:
81287c13493SDavid Brownell 		led_on  = 1 << 7;
81387c13493SDavid Brownell 		led_per = 1 << 7;
81487c13493SDavid Brownell 		break;
81587c13493SDavid Brownell 	case BLINK:
81687c13493SDavid Brownell 		led_on  = 0x30 | (0 << 7);
81787c13493SDavid Brownell 		led_per = 0x08 | (1 << 7);
81887c13493SDavid Brownell 		break;
81987c13493SDavid Brownell 	default:
82087c13493SDavid Brownell 		printk(KERN_ERR "%s: Wrong mode parameter for set_led()\n",
82187c13493SDavid Brownell 		       DRIVER_NAME);
82287c13493SDavid Brownell 		mutex_unlock(&the_tps->lock);
82387c13493SDavid Brownell 		return -EINVAL;
82487c13493SDavid Brownell 	}
82587c13493SDavid Brownell 
82687c13493SDavid Brownell 	status = i2c_smbus_write_byte_data(the_tps->client,
82787c13493SDavid Brownell 			TPS_LED1_ON + offs, led_on);
82887c13493SDavid Brownell 
82987c13493SDavid Brownell 	if (status != 0) {
83087c13493SDavid Brownell 		printk(KERN_ERR "%s: Failed to write led%i_on register\n",
83187c13493SDavid Brownell 		       DRIVER_NAME, led);
83287c13493SDavid Brownell 		mutex_unlock(&the_tps->lock);
83387c13493SDavid Brownell 		return status;
83487c13493SDavid Brownell 	}
83587c13493SDavid Brownell 
83687c13493SDavid Brownell 	pr_debug("%s: led%i_on   0x%02x\n", DRIVER_NAME, led,
83787c13493SDavid Brownell 		i2c_smbus_read_byte_data(the_tps->client, TPS_LED1_ON + offs));
83887c13493SDavid Brownell 
83987c13493SDavid Brownell 	status = i2c_smbus_write_byte_data(the_tps->client,
84087c13493SDavid Brownell 			TPS_LED1_PER + offs, led_per);
84187c13493SDavid Brownell 
84287c13493SDavid Brownell 	if (status != 0) {
84387c13493SDavid Brownell 		printk(KERN_ERR "%s: Failed to write led%i_per register\n",
84487c13493SDavid Brownell 		       DRIVER_NAME, led);
84587c13493SDavid Brownell 		mutex_unlock(&the_tps->lock);
84687c13493SDavid Brownell 		return status;
84787c13493SDavid Brownell 	}
84887c13493SDavid Brownell 
84987c13493SDavid Brownell 	pr_debug("%s: led%i_per  0x%02x\n", DRIVER_NAME, led,
85087c13493SDavid Brownell 		i2c_smbus_read_byte_data(the_tps->client,
85187c13493SDavid Brownell 				TPS_LED1_PER + offs));
85287c13493SDavid Brownell 
85387c13493SDavid Brownell 	mutex_unlock(&the_tps->lock);
85487c13493SDavid Brownell 
85587c13493SDavid Brownell 	return status;
85687c13493SDavid Brownell }
85787c13493SDavid Brownell EXPORT_SYMBOL(tps65010_set_led);
85887c13493SDavid Brownell 
85987c13493SDavid Brownell /*-------------------------------------------------------------------------*/
86087c13493SDavid Brownell /* tps65010_set_vib parameter:
86187c13493SDavid Brownell  * value: ON or OFF
86287c13493SDavid Brownell  */
86387c13493SDavid Brownell int tps65010_set_vib(unsigned value)
86487c13493SDavid Brownell {
86587c13493SDavid Brownell 	int	 status;
86687c13493SDavid Brownell 	unsigned vdcdc2;
86787c13493SDavid Brownell 
86887c13493SDavid Brownell 	if (!the_tps)
86987c13493SDavid Brownell 		return -ENODEV;
87087c13493SDavid Brownell 
87187c13493SDavid Brownell 	mutex_lock(&the_tps->lock);
87287c13493SDavid Brownell 
87387c13493SDavid Brownell 	vdcdc2 = i2c_smbus_read_byte_data(the_tps->client, TPS_VDCDC2);
87487c13493SDavid Brownell 	vdcdc2 &= ~(1 << 1);
87587c13493SDavid Brownell 	if (value)
87687c13493SDavid Brownell 		vdcdc2 |= (1 << 1);
87787c13493SDavid Brownell 	status = i2c_smbus_write_byte_data(the_tps->client,
87887c13493SDavid Brownell 		TPS_VDCDC2, vdcdc2);
87987c13493SDavid Brownell 
88087c13493SDavid Brownell 	pr_debug("%s: vibrator %s\n", DRIVER_NAME, value ? "on" : "off");
88187c13493SDavid Brownell 
88287c13493SDavid Brownell 	mutex_unlock(&the_tps->lock);
88387c13493SDavid Brownell 	return status;
88487c13493SDavid Brownell }
88587c13493SDavid Brownell EXPORT_SYMBOL(tps65010_set_vib);
88687c13493SDavid Brownell 
88787c13493SDavid Brownell /*-------------------------------------------------------------------------*/
88887c13493SDavid Brownell /* tps65010_set_low_pwr parameter:
88987c13493SDavid Brownell  * mode: ON or OFF
89087c13493SDavid Brownell  */
89187c13493SDavid Brownell int tps65010_set_low_pwr(unsigned mode)
89287c13493SDavid Brownell {
89387c13493SDavid Brownell 	int	 status;
89487c13493SDavid Brownell 	unsigned vdcdc1;
89587c13493SDavid Brownell 
89687c13493SDavid Brownell 	if (!the_tps)
89787c13493SDavid Brownell 		return -ENODEV;
89887c13493SDavid Brownell 
89987c13493SDavid Brownell 	mutex_lock(&the_tps->lock);
90087c13493SDavid Brownell 
90187c13493SDavid Brownell 	pr_debug("%s: %s low_pwr, vdcdc1 0x%02x\n", DRIVER_NAME,
90287c13493SDavid Brownell 		mode ? "enable" : "disable",
90387c13493SDavid Brownell 		i2c_smbus_read_byte_data(the_tps->client, TPS_VDCDC1));
90487c13493SDavid Brownell 
90587c13493SDavid Brownell 	vdcdc1 = i2c_smbus_read_byte_data(the_tps->client, TPS_VDCDC1);
90687c13493SDavid Brownell 
90787c13493SDavid Brownell 	switch (mode) {
90887c13493SDavid Brownell 	case OFF:
90987c13493SDavid Brownell 		vdcdc1 &= ~TPS_ENABLE_LP; /* disable ENABLE_LP bit */
91087c13493SDavid Brownell 		break;
91187c13493SDavid Brownell 	/* case ON: */
91287c13493SDavid Brownell 	default:
91387c13493SDavid Brownell 		vdcdc1 |= TPS_ENABLE_LP;  /* enable ENABLE_LP bit */
91487c13493SDavid Brownell 		break;
91587c13493SDavid Brownell 	}
91687c13493SDavid Brownell 
91787c13493SDavid Brownell 	status = i2c_smbus_write_byte_data(the_tps->client,
91887c13493SDavid Brownell 			TPS_VDCDC1, vdcdc1);
91987c13493SDavid Brownell 
92087c13493SDavid Brownell 	if (status != 0)
92187c13493SDavid Brownell 		printk(KERN_ERR "%s: Failed to write vdcdc1 register\n",
92287c13493SDavid Brownell 			DRIVER_NAME);
92387c13493SDavid Brownell 	else
92487c13493SDavid Brownell 		pr_debug("%s: vdcdc1 0x%02x\n", DRIVER_NAME,
92587c13493SDavid Brownell 			i2c_smbus_read_byte_data(the_tps->client, TPS_VDCDC1));
92687c13493SDavid Brownell 
92787c13493SDavid Brownell 	mutex_unlock(&the_tps->lock);
92887c13493SDavid Brownell 
92987c13493SDavid Brownell 	return status;
93087c13493SDavid Brownell }
93187c13493SDavid Brownell EXPORT_SYMBOL(tps65010_set_low_pwr);
93287c13493SDavid Brownell 
93387c13493SDavid Brownell /*-------------------------------------------------------------------------*/
93487c13493SDavid Brownell /* tps65010_config_vregs1 parameter:
93587c13493SDavid Brownell  * value to be written to VREGS1 register
93687c13493SDavid Brownell  * Note: The complete register is written, set all bits you need
93787c13493SDavid Brownell  */
93887c13493SDavid Brownell int tps65010_config_vregs1(unsigned value)
93987c13493SDavid Brownell {
94087c13493SDavid Brownell 	int	 status;
94187c13493SDavid Brownell 
94287c13493SDavid Brownell 	if (!the_tps)
94387c13493SDavid Brownell 		return -ENODEV;
94487c13493SDavid Brownell 
94587c13493SDavid Brownell 	mutex_lock(&the_tps->lock);
94687c13493SDavid Brownell 
94787c13493SDavid Brownell 	pr_debug("%s: vregs1 0x%02x\n", DRIVER_NAME,
94887c13493SDavid Brownell 			i2c_smbus_read_byte_data(the_tps->client, TPS_VREGS1));
94987c13493SDavid Brownell 
95087c13493SDavid Brownell 	status = i2c_smbus_write_byte_data(the_tps->client,
95187c13493SDavid Brownell 			TPS_VREGS1, value);
95287c13493SDavid Brownell 
95387c13493SDavid Brownell 	if (status != 0)
95487c13493SDavid Brownell 		printk(KERN_ERR "%s: Failed to write vregs1 register\n",
95587c13493SDavid Brownell 			DRIVER_NAME);
95687c13493SDavid Brownell 	else
95787c13493SDavid Brownell 		pr_debug("%s: vregs1 0x%02x\n", DRIVER_NAME,
95887c13493SDavid Brownell 			i2c_smbus_read_byte_data(the_tps->client, TPS_VREGS1));
95987c13493SDavid Brownell 
96087c13493SDavid Brownell 	mutex_unlock(&the_tps->lock);
96187c13493SDavid Brownell 
96287c13493SDavid Brownell 	return status;
96387c13493SDavid Brownell }
96487c13493SDavid Brownell EXPORT_SYMBOL(tps65010_config_vregs1);
96587c13493SDavid Brownell 
966b45440c3SBen Dooks int tps65010_config_vdcdc2(unsigned value)
967b45440c3SBen Dooks {
968b45440c3SBen Dooks 	struct i2c_client *c;
969b45440c3SBen Dooks 	int	 status;
970b45440c3SBen Dooks 
971b45440c3SBen Dooks 	if (!the_tps)
972b45440c3SBen Dooks 		return -ENODEV;
973b45440c3SBen Dooks 
974b45440c3SBen Dooks 	c = the_tps->client;
975b45440c3SBen Dooks 	mutex_lock(&the_tps->lock);
976b45440c3SBen Dooks 
977b45440c3SBen Dooks 	pr_debug("%s: vdcdc2 0x%02x\n", DRIVER_NAME,
978b45440c3SBen Dooks 		 i2c_smbus_read_byte_data(c, TPS_VDCDC2));
979b45440c3SBen Dooks 
980b45440c3SBen Dooks 	status = i2c_smbus_write_byte_data(c, TPS_VDCDC2, value);
981b45440c3SBen Dooks 
982b45440c3SBen Dooks 	if (status != 0)
983b45440c3SBen Dooks 		printk(KERN_ERR "%s: Failed to write vdcdc2 register\n",
984b45440c3SBen Dooks 			DRIVER_NAME);
985b45440c3SBen Dooks 	else
986b45440c3SBen Dooks 		pr_debug("%s: vregs1 0x%02x\n", DRIVER_NAME,
987b45440c3SBen Dooks 			 i2c_smbus_read_byte_data(c, TPS_VDCDC2));
988b45440c3SBen Dooks 
989b45440c3SBen Dooks 	mutex_unlock(&the_tps->lock);
990b45440c3SBen Dooks 	return status;
991b45440c3SBen Dooks }
992b45440c3SBen Dooks EXPORT_SYMBOL(tps65010_config_vdcdc2);
993b45440c3SBen Dooks 
99487c13493SDavid Brownell /*-------------------------------------------------------------------------*/
99587c13493SDavid Brownell /* tps65013_set_low_pwr parameter:
99687c13493SDavid Brownell  * mode: ON or OFF
99787c13493SDavid Brownell  */
99887c13493SDavid Brownell 
99987c13493SDavid Brownell /* FIXME: Assumes AC or USB power is present. Setting AUA bit is not
100087c13493SDavid Brownell 	required if power supply is through a battery */
100187c13493SDavid Brownell 
100287c13493SDavid Brownell int tps65013_set_low_pwr(unsigned mode)
100387c13493SDavid Brownell {
100487c13493SDavid Brownell 	int	 status;
100587c13493SDavid Brownell 	unsigned vdcdc1, chgconfig;
100687c13493SDavid Brownell 
100787c13493SDavid Brownell 	if (!the_tps || the_tps->por)
100887c13493SDavid Brownell 		return -ENODEV;
100987c13493SDavid Brownell 
101087c13493SDavid Brownell 	mutex_lock(&the_tps->lock);
101187c13493SDavid Brownell 
101287c13493SDavid Brownell 	pr_debug("%s: %s low_pwr, chgconfig 0x%02x vdcdc1 0x%02x\n",
101387c13493SDavid Brownell 		DRIVER_NAME,
101487c13493SDavid Brownell 		mode ? "enable" : "disable",
101587c13493SDavid Brownell 		i2c_smbus_read_byte_data(the_tps->client, TPS_CHGCONFIG),
101687c13493SDavid Brownell 		i2c_smbus_read_byte_data(the_tps->client, TPS_VDCDC1));
101787c13493SDavid Brownell 
101887c13493SDavid Brownell 	chgconfig = i2c_smbus_read_byte_data(the_tps->client, TPS_CHGCONFIG);
101987c13493SDavid Brownell 	vdcdc1 = i2c_smbus_read_byte_data(the_tps->client, TPS_VDCDC1);
102087c13493SDavid Brownell 
102187c13493SDavid Brownell 	switch (mode) {
102287c13493SDavid Brownell 	case OFF:
102387c13493SDavid Brownell 		chgconfig &= ~TPS65013_AUA; /* disable AUA bit */
102487c13493SDavid Brownell 		vdcdc1 &= ~TPS_ENABLE_LP; /* disable ENABLE_LP bit */
102587c13493SDavid Brownell 		break;
102687c13493SDavid Brownell 	/* case ON: */
102787c13493SDavid Brownell 	default:
102887c13493SDavid Brownell 		chgconfig |= TPS65013_AUA;  /* enable AUA bit */
102987c13493SDavid Brownell 		vdcdc1 |= TPS_ENABLE_LP;  /* enable ENABLE_LP bit */
103087c13493SDavid Brownell 		break;
103187c13493SDavid Brownell 	}
103287c13493SDavid Brownell 
103387c13493SDavid Brownell 	status = i2c_smbus_write_byte_data(the_tps->client,
103487c13493SDavid Brownell 			TPS_CHGCONFIG, chgconfig);
103587c13493SDavid Brownell 	if (status != 0) {
103687c13493SDavid Brownell 		printk(KERN_ERR "%s: Failed to write chconfig register\n",
103787c13493SDavid Brownell 	 DRIVER_NAME);
103887c13493SDavid Brownell 		mutex_unlock(&the_tps->lock);
103987c13493SDavid Brownell 		return status;
104087c13493SDavid Brownell 	}
104187c13493SDavid Brownell 
104287c13493SDavid Brownell 	chgconfig = i2c_smbus_read_byte_data(the_tps->client, TPS_CHGCONFIG);
104387c13493SDavid Brownell 	the_tps->chgconf = chgconfig;
104487c13493SDavid Brownell 	show_chgconfig(0, "chgconf", chgconfig);
104587c13493SDavid Brownell 
104687c13493SDavid Brownell 	status = i2c_smbus_write_byte_data(the_tps->client,
104787c13493SDavid Brownell 			TPS_VDCDC1, vdcdc1);
104887c13493SDavid Brownell 
104987c13493SDavid Brownell 	if (status != 0)
105087c13493SDavid Brownell 		printk(KERN_ERR "%s: Failed to write vdcdc1 register\n",
105187c13493SDavid Brownell 	 DRIVER_NAME);
105287c13493SDavid Brownell 	else
105387c13493SDavid Brownell 		pr_debug("%s: vdcdc1 0x%02x\n", DRIVER_NAME,
105487c13493SDavid Brownell 			i2c_smbus_read_byte_data(the_tps->client, TPS_VDCDC1));
105587c13493SDavid Brownell 
105687c13493SDavid Brownell 	mutex_unlock(&the_tps->lock);
105787c13493SDavid Brownell 
105887c13493SDavid Brownell 	return status;
105987c13493SDavid Brownell }
106087c13493SDavid Brownell EXPORT_SYMBOL(tps65013_set_low_pwr);
106187c13493SDavid Brownell 
106287c13493SDavid Brownell /*-------------------------------------------------------------------------*/
106387c13493SDavid Brownell 
106487c13493SDavid Brownell static int __init tps_init(void)
106587c13493SDavid Brownell {
106687c13493SDavid Brownell 	u32	tries = 3;
106787c13493SDavid Brownell 	int	status = -ENODEV;
106887c13493SDavid Brownell 
106987c13493SDavid Brownell 	printk(KERN_INFO "%s: version %s\n", DRIVER_NAME, DRIVER_VERSION);
107087c13493SDavid Brownell 
107187c13493SDavid Brownell 	/* some boards have startup glitches */
107287c13493SDavid Brownell 	while (tries--) {
107387c13493SDavid Brownell 		status = i2c_add_driver(&tps65010_driver);
107487c13493SDavid Brownell 		if (the_tps)
107587c13493SDavid Brownell 			break;
107687c13493SDavid Brownell 		i2c_del_driver(&tps65010_driver);
107787c13493SDavid Brownell 		if (!tries) {
107887c13493SDavid Brownell 			printk(KERN_ERR "%s: no chip?\n", DRIVER_NAME);
107987c13493SDavid Brownell 			return -ENODEV;
108087c13493SDavid Brownell 		}
108187c13493SDavid Brownell 		pr_debug("%s: re-probe ...\n", DRIVER_NAME);
108287c13493SDavid Brownell 		msleep(10);
108387c13493SDavid Brownell 	}
108487c13493SDavid Brownell 
108587c13493SDavid Brownell 	return status;
108687c13493SDavid Brownell }
108787c13493SDavid Brownell /* NOTE:  this MUST be initialized before the other parts of the system
108887c13493SDavid Brownell  * that rely on it ... but after the i2c bus on which this relies.
108987c13493SDavid Brownell  * That is, much earlier than on PC-type systems, which don't often use
109087c13493SDavid Brownell  * I2C as a core system bus.
109187c13493SDavid Brownell  */
109287c13493SDavid Brownell subsys_initcall(tps_init);
109387c13493SDavid Brownell 
109487c13493SDavid Brownell static void __exit tps_exit(void)
109587c13493SDavid Brownell {
109687c13493SDavid Brownell 	i2c_del_driver(&tps65010_driver);
109787c13493SDavid Brownell }
109887c13493SDavid Brownell module_exit(tps_exit);
109987c13493SDavid Brownell 
1100