18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * tps65010 - driver for tps6501x power management chips 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2004 Texas Instruments 68c2ecf20Sopenharmony_ci * Copyright (C) 2004-2005 David Brownell 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/kernel.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/init.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 148c2ecf20Sopenharmony_ci#include <linux/i2c.h> 158c2ecf20Sopenharmony_ci#include <linux/delay.h> 168c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 178c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 188c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 198c2ecf20Sopenharmony_ci#include <linux/mutex.h> 208c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include <linux/mfd/tps65010.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include <linux/gpio/driver.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/ 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define DRIVER_VERSION "2 May 2005" 308c2ecf20Sopenharmony_ci#define DRIVER_NAME (tps65010_driver.driver.name) 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("TPS6501x Power Management Driver"); 338c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic struct i2c_driver tps65010_driver; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/ 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/* This driver handles a family of multipurpose chips, which incorporate 408c2ecf20Sopenharmony_ci * voltage regulators, lithium ion/polymer battery charging, GPIOs, LEDs, 418c2ecf20Sopenharmony_ci * and other features often needed in portable devices like cell phones 428c2ecf20Sopenharmony_ci * or digital cameras. 438c2ecf20Sopenharmony_ci * 448c2ecf20Sopenharmony_ci * The tps65011 and tps65013 have different voltage settings compared 458c2ecf20Sopenharmony_ci * to tps65010 and tps65012. The tps65013 has a NO_CHG status/irq. 468c2ecf20Sopenharmony_ci * All except tps65010 have "wait" mode, possibly defaulted so that 478c2ecf20Sopenharmony_ci * battery-insert != device-on. 488c2ecf20Sopenharmony_ci * 498c2ecf20Sopenharmony_ci * We could distinguish between some models by checking VDCDC1.UVLO or 508c2ecf20Sopenharmony_ci * other registers, unless they've been changed already after powerup 518c2ecf20Sopenharmony_ci * as part of board setup by a bootloader. 528c2ecf20Sopenharmony_ci */ 538c2ecf20Sopenharmony_cienum tps_model { 548c2ecf20Sopenharmony_ci TPS65010, 558c2ecf20Sopenharmony_ci TPS65011, 568c2ecf20Sopenharmony_ci TPS65012, 578c2ecf20Sopenharmony_ci TPS65013, 588c2ecf20Sopenharmony_ci}; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistruct tps65010 { 618c2ecf20Sopenharmony_ci struct i2c_client *client; 628c2ecf20Sopenharmony_ci struct mutex lock; 638c2ecf20Sopenharmony_ci struct delayed_work work; 648c2ecf20Sopenharmony_ci struct dentry *file; 658c2ecf20Sopenharmony_ci unsigned charging:1; 668c2ecf20Sopenharmony_ci unsigned por:1; 678c2ecf20Sopenharmony_ci unsigned model:8; 688c2ecf20Sopenharmony_ci u16 vbus; 698c2ecf20Sopenharmony_ci unsigned long flags; 708c2ecf20Sopenharmony_ci#define FLAG_VBUS_CHANGED 0 718c2ecf20Sopenharmony_ci#define FLAG_IRQ_ENABLE 1 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci /* copies of last register state */ 748c2ecf20Sopenharmony_ci u8 chgstatus, regstatus, chgconf; 758c2ecf20Sopenharmony_ci u8 nmask1, nmask2; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci u8 outmask; 788c2ecf20Sopenharmony_ci struct gpio_chip chip; 798c2ecf20Sopenharmony_ci struct platform_device *leds; 808c2ecf20Sopenharmony_ci}; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci#define POWER_POLL_DELAY msecs_to_jiffies(5000) 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/ 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci#if defined(DEBUG) || defined(CONFIG_DEBUG_FS) 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic void dbg_chgstat(char *buf, size_t len, u8 chgstatus) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci snprintf(buf, len, "%02x%s%s%s%s%s%s%s%s\n", 918c2ecf20Sopenharmony_ci chgstatus, 928c2ecf20Sopenharmony_ci (chgstatus & TPS_CHG_USB) ? " USB" : "", 938c2ecf20Sopenharmony_ci (chgstatus & TPS_CHG_AC) ? " AC" : "", 948c2ecf20Sopenharmony_ci (chgstatus & TPS_CHG_THERM) ? " therm" : "", 958c2ecf20Sopenharmony_ci (chgstatus & TPS_CHG_TERM) ? " done" : 968c2ecf20Sopenharmony_ci ((chgstatus & (TPS_CHG_USB|TPS_CHG_AC)) 978c2ecf20Sopenharmony_ci ? " (charging)" : ""), 988c2ecf20Sopenharmony_ci (chgstatus & TPS_CHG_TAPER_TMO) ? " taper_tmo" : "", 998c2ecf20Sopenharmony_ci (chgstatus & TPS_CHG_CHG_TMO) ? " charge_tmo" : "", 1008c2ecf20Sopenharmony_ci (chgstatus & TPS_CHG_PRECHG_TMO) ? " prechg_tmo" : "", 1018c2ecf20Sopenharmony_ci (chgstatus & TPS_CHG_TEMP_ERR) ? " temp_err" : ""); 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic void dbg_regstat(char *buf, size_t len, u8 regstatus) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci snprintf(buf, len, "%02x %s%s%s%s%s%s%s%s\n", 1078c2ecf20Sopenharmony_ci regstatus, 1088c2ecf20Sopenharmony_ci (regstatus & TPS_REG_ONOFF) ? "off" : "(on)", 1098c2ecf20Sopenharmony_ci (regstatus & TPS_REG_COVER) ? " uncover" : "", 1108c2ecf20Sopenharmony_ci (regstatus & TPS_REG_UVLO) ? " UVLO" : "", 1118c2ecf20Sopenharmony_ci (regstatus & TPS_REG_NO_CHG) ? " NO_CHG" : "", 1128c2ecf20Sopenharmony_ci (regstatus & TPS_REG_PG_LD02) ? " ld02_bad" : "", 1138c2ecf20Sopenharmony_ci (regstatus & TPS_REG_PG_LD01) ? " ld01_bad" : "", 1148c2ecf20Sopenharmony_ci (regstatus & TPS_REG_PG_MAIN) ? " main_bad" : "", 1158c2ecf20Sopenharmony_ci (regstatus & TPS_REG_PG_CORE) ? " core_bad" : ""); 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic void dbg_chgconf(int por, char *buf, size_t len, u8 chgconfig) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci const char *hibit; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci if (por) 1238c2ecf20Sopenharmony_ci hibit = (chgconfig & TPS_CHARGE_POR) 1248c2ecf20Sopenharmony_ci ? "POR=69ms" : "POR=1sec"; 1258c2ecf20Sopenharmony_ci else 1268c2ecf20Sopenharmony_ci hibit = (chgconfig & TPS65013_AUA) ? "AUA" : ""; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci snprintf(buf, len, "%02x %s%s%s AC=%d%% USB=%dmA %sCharge\n", 1298c2ecf20Sopenharmony_ci chgconfig, hibit, 1308c2ecf20Sopenharmony_ci (chgconfig & TPS_CHARGE_RESET) ? " reset" : "", 1318c2ecf20Sopenharmony_ci (chgconfig & TPS_CHARGE_FAST) ? " fast" : "", 1328c2ecf20Sopenharmony_ci ({int p; switch ((chgconfig >> 3) & 3) { 1338c2ecf20Sopenharmony_ci case 3: p = 100; break; 1348c2ecf20Sopenharmony_ci case 2: p = 75; break; 1358c2ecf20Sopenharmony_ci case 1: p = 50; break; 1368c2ecf20Sopenharmony_ci default: p = 25; break; 1378c2ecf20Sopenharmony_ci }; p; }), 1388c2ecf20Sopenharmony_ci (chgconfig & TPS_VBUS_CHARGING) 1398c2ecf20Sopenharmony_ci ? ((chgconfig & TPS_VBUS_500MA) ? 500 : 100) 1408c2ecf20Sopenharmony_ci : 0, 1418c2ecf20Sopenharmony_ci (chgconfig & TPS_CHARGE_ENABLE) ? "" : "No"); 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci#endif 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci#ifdef DEBUG 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic void show_chgstatus(const char *label, u8 chgstatus) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci char buf [100]; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci dbg_chgstat(buf, sizeof buf, chgstatus); 1538c2ecf20Sopenharmony_ci pr_debug("%s: %s %s", DRIVER_NAME, label, buf); 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic void show_regstatus(const char *label, u8 regstatus) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci char buf [100]; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci dbg_regstat(buf, sizeof buf, regstatus); 1618c2ecf20Sopenharmony_ci pr_debug("%s: %s %s", DRIVER_NAME, label, buf); 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic void show_chgconfig(int por, const char *label, u8 chgconfig) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci char buf [100]; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci dbg_chgconf(por, buf, sizeof buf, chgconfig); 1698c2ecf20Sopenharmony_ci pr_debug("%s: %s %s", DRIVER_NAME, label, buf); 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci#else 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic inline void show_chgstatus(const char *label, u8 chgstatus) { } 1758c2ecf20Sopenharmony_cistatic inline void show_regstatus(const char *label, u8 chgstatus) { } 1768c2ecf20Sopenharmony_cistatic inline void show_chgconfig(int por, const char *label, u8 chgconfig) { } 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci#endif 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic int dbg_show(struct seq_file *s, void *_) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci struct tps65010 *tps = s->private; 1858c2ecf20Sopenharmony_ci u8 value, v2; 1868c2ecf20Sopenharmony_ci unsigned i; 1878c2ecf20Sopenharmony_ci char buf[100]; 1888c2ecf20Sopenharmony_ci const char *chip; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci switch (tps->model) { 1918c2ecf20Sopenharmony_ci case TPS65010: chip = "tps65010"; break; 1928c2ecf20Sopenharmony_ci case TPS65011: chip = "tps65011"; break; 1938c2ecf20Sopenharmony_ci case TPS65012: chip = "tps65012"; break; 1948c2ecf20Sopenharmony_ci case TPS65013: chip = "tps65013"; break; 1958c2ecf20Sopenharmony_ci default: chip = NULL; break; 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci seq_printf(s, "driver %s\nversion %s\nchip %s\n\n", 1988c2ecf20Sopenharmony_ci DRIVER_NAME, DRIVER_VERSION, chip); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci mutex_lock(&tps->lock); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci /* FIXME how can we tell whether a battery is present? 2038c2ecf20Sopenharmony_ci * likely involves a charge gauging chip (like BQ26501). 2048c2ecf20Sopenharmony_ci */ 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci seq_printf(s, "%scharging\n\n", tps->charging ? "" : "(not) "); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci /* registers for monitoring battery charging and status; note 2108c2ecf20Sopenharmony_ci * that reading chgstat and regstat may ack IRQs... 2118c2ecf20Sopenharmony_ci */ 2128c2ecf20Sopenharmony_ci value = i2c_smbus_read_byte_data(tps->client, TPS_CHGCONFIG); 2138c2ecf20Sopenharmony_ci dbg_chgconf(tps->por, buf, sizeof buf, value); 2148c2ecf20Sopenharmony_ci seq_printf(s, "chgconfig %s", buf); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci value = i2c_smbus_read_byte_data(tps->client, TPS_CHGSTATUS); 2178c2ecf20Sopenharmony_ci dbg_chgstat(buf, sizeof buf, value); 2188c2ecf20Sopenharmony_ci seq_printf(s, "chgstat %s", buf); 2198c2ecf20Sopenharmony_ci value = i2c_smbus_read_byte_data(tps->client, TPS_MASK1); 2208c2ecf20Sopenharmony_ci dbg_chgstat(buf, sizeof buf, value); 2218c2ecf20Sopenharmony_ci seq_printf(s, "mask1 %s", buf); 2228c2ecf20Sopenharmony_ci /* ignore ackint1 */ 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci value = i2c_smbus_read_byte_data(tps->client, TPS_REGSTATUS); 2258c2ecf20Sopenharmony_ci dbg_regstat(buf, sizeof buf, value); 2268c2ecf20Sopenharmony_ci seq_printf(s, "regstat %s", buf); 2278c2ecf20Sopenharmony_ci value = i2c_smbus_read_byte_data(tps->client, TPS_MASK2); 2288c2ecf20Sopenharmony_ci dbg_regstat(buf, sizeof buf, value); 2298c2ecf20Sopenharmony_ci seq_printf(s, "mask2 %s\n", buf); 2308c2ecf20Sopenharmony_ci /* ignore ackint2 */ 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci queue_delayed_work(system_power_efficient_wq, &tps->work, 2338c2ecf20Sopenharmony_ci POWER_POLL_DELAY); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci /* VMAIN voltage, enable lowpower, etc */ 2368c2ecf20Sopenharmony_ci value = i2c_smbus_read_byte_data(tps->client, TPS_VDCDC1); 2378c2ecf20Sopenharmony_ci seq_printf(s, "vdcdc1 %02x\n", value); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci /* VCORE voltage, vibrator on/off */ 2408c2ecf20Sopenharmony_ci value = i2c_smbus_read_byte_data(tps->client, TPS_VDCDC2); 2418c2ecf20Sopenharmony_ci seq_printf(s, "vdcdc2 %02x\n", value); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci /* both LD0s, and their lowpower behavior */ 2448c2ecf20Sopenharmony_ci value = i2c_smbus_read_byte_data(tps->client, TPS_VREGS1); 2458c2ecf20Sopenharmony_ci seq_printf(s, "vregs1 %02x\n\n", value); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci /* LEDs and GPIOs */ 2498c2ecf20Sopenharmony_ci value = i2c_smbus_read_byte_data(tps->client, TPS_LED1_ON); 2508c2ecf20Sopenharmony_ci v2 = i2c_smbus_read_byte_data(tps->client, TPS_LED1_PER); 2518c2ecf20Sopenharmony_ci seq_printf(s, "led1 %s, on=%02x, per=%02x, %d/%d msec\n", 2528c2ecf20Sopenharmony_ci (value & 0x80) 2538c2ecf20Sopenharmony_ci ? ((v2 & 0x80) ? "on" : "off") 2548c2ecf20Sopenharmony_ci : ((v2 & 0x80) ? "blink" : "(nPG)"), 2558c2ecf20Sopenharmony_ci value, v2, 2568c2ecf20Sopenharmony_ci (value & 0x7f) * 10, (v2 & 0x7f) * 100); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci value = i2c_smbus_read_byte_data(tps->client, TPS_LED2_ON); 2598c2ecf20Sopenharmony_ci v2 = i2c_smbus_read_byte_data(tps->client, TPS_LED2_PER); 2608c2ecf20Sopenharmony_ci seq_printf(s, "led2 %s, on=%02x, per=%02x, %d/%d msec\n", 2618c2ecf20Sopenharmony_ci (value & 0x80) 2628c2ecf20Sopenharmony_ci ? ((v2 & 0x80) ? "on" : "off") 2638c2ecf20Sopenharmony_ci : ((v2 & 0x80) ? "blink" : "off"), 2648c2ecf20Sopenharmony_ci value, v2, 2658c2ecf20Sopenharmony_ci (value & 0x7f) * 10, (v2 & 0x7f) * 100); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci value = i2c_smbus_read_byte_data(tps->client, TPS_DEFGPIO); 2688c2ecf20Sopenharmony_ci v2 = i2c_smbus_read_byte_data(tps->client, TPS_MASK3); 2698c2ecf20Sopenharmony_ci seq_printf(s, "defgpio %02x mask3 %02x\n", value, v2); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) { 2728c2ecf20Sopenharmony_ci if (value & (1 << (4 + i))) 2738c2ecf20Sopenharmony_ci seq_printf(s, " gpio%d-out %s\n", i + 1, 2748c2ecf20Sopenharmony_ci (value & (1 << i)) ? "low" : "hi "); 2758c2ecf20Sopenharmony_ci else 2768c2ecf20Sopenharmony_ci seq_printf(s, " gpio%d-in %s %s %s\n", i + 1, 2778c2ecf20Sopenharmony_ci (value & (1 << i)) ? "hi " : "low", 2788c2ecf20Sopenharmony_ci (v2 & (1 << i)) ? "no-irq" : "irq", 2798c2ecf20Sopenharmony_ci (v2 & (1 << (4 + i))) ? "rising" : "falling"); 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci mutex_unlock(&tps->lock); 2838c2ecf20Sopenharmony_ci return 0; 2848c2ecf20Sopenharmony_ci} 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_cistatic int dbg_tps_open(struct inode *inode, struct file *file) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci return single_open(file, dbg_show, inode->i_private); 2898c2ecf20Sopenharmony_ci} 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_cistatic const struct file_operations debug_fops = { 2928c2ecf20Sopenharmony_ci .open = dbg_tps_open, 2938c2ecf20Sopenharmony_ci .read = seq_read, 2948c2ecf20Sopenharmony_ci .llseek = seq_lseek, 2958c2ecf20Sopenharmony_ci .release = single_release, 2968c2ecf20Sopenharmony_ci}; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci#define DEBUG_FOPS &debug_fops 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci#else 3018c2ecf20Sopenharmony_ci#define DEBUG_FOPS NULL 3028c2ecf20Sopenharmony_ci#endif 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/ 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci/* handle IRQS in a task context, so we can use I2C calls */ 3078c2ecf20Sopenharmony_cistatic void tps65010_interrupt(struct tps65010 *tps) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci u8 tmp = 0, mask, poll; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci /* IRQs won't trigger for certain events, but we can get 3128c2ecf20Sopenharmony_ci * others by polling (normally, with external power applied). 3138c2ecf20Sopenharmony_ci */ 3148c2ecf20Sopenharmony_ci poll = 0; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci /* regstatus irqs */ 3178c2ecf20Sopenharmony_ci if (tps->nmask2) { 3188c2ecf20Sopenharmony_ci tmp = i2c_smbus_read_byte_data(tps->client, TPS_REGSTATUS); 3198c2ecf20Sopenharmony_ci mask = tmp ^ tps->regstatus; 3208c2ecf20Sopenharmony_ci tps->regstatus = tmp; 3218c2ecf20Sopenharmony_ci mask &= tps->nmask2; 3228c2ecf20Sopenharmony_ci } else 3238c2ecf20Sopenharmony_ci mask = 0; 3248c2ecf20Sopenharmony_ci if (mask) { 3258c2ecf20Sopenharmony_ci tps->regstatus = tmp; 3268c2ecf20Sopenharmony_ci /* may need to shut something down ... */ 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci /* "off" usually means deep sleep */ 3298c2ecf20Sopenharmony_ci if (tmp & TPS_REG_ONOFF) { 3308c2ecf20Sopenharmony_ci pr_info("%s: power off button\n", DRIVER_NAME); 3318c2ecf20Sopenharmony_ci#if 0 3328c2ecf20Sopenharmony_ci /* REVISIT: this might need its own workqueue 3338c2ecf20Sopenharmony_ci * plus tweaks including deadlock avoidance ... 3348c2ecf20Sopenharmony_ci * also needs to get error handling and probably 3358c2ecf20Sopenharmony_ci * an #ifdef CONFIG_HIBERNATION 3368c2ecf20Sopenharmony_ci */ 3378c2ecf20Sopenharmony_ci hibernate(); 3388c2ecf20Sopenharmony_ci#endif 3398c2ecf20Sopenharmony_ci poll = 1; 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci /* chgstatus irqs */ 3448c2ecf20Sopenharmony_ci if (tps->nmask1) { 3458c2ecf20Sopenharmony_ci tmp = i2c_smbus_read_byte_data(tps->client, TPS_CHGSTATUS); 3468c2ecf20Sopenharmony_ci mask = tmp ^ tps->chgstatus; 3478c2ecf20Sopenharmony_ci tps->chgstatus = tmp; 3488c2ecf20Sopenharmony_ci mask &= tps->nmask1; 3498c2ecf20Sopenharmony_ci } else 3508c2ecf20Sopenharmony_ci mask = 0; 3518c2ecf20Sopenharmony_ci if (mask) { 3528c2ecf20Sopenharmony_ci unsigned charging = 0; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci show_chgstatus("chg/irq", tmp); 3558c2ecf20Sopenharmony_ci if (tmp & (TPS_CHG_USB|TPS_CHG_AC)) 3568c2ecf20Sopenharmony_ci show_chgconfig(tps->por, "conf", tps->chgconf); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci /* Unless it was turned off or disabled, we charge any 3598c2ecf20Sopenharmony_ci * battery whenever there's power available for it 3608c2ecf20Sopenharmony_ci * and the charger hasn't been disabled. 3618c2ecf20Sopenharmony_ci */ 3628c2ecf20Sopenharmony_ci if (!(tps->chgstatus & ~(TPS_CHG_USB|TPS_CHG_AC)) 3638c2ecf20Sopenharmony_ci && (tps->chgstatus & (TPS_CHG_USB|TPS_CHG_AC)) 3648c2ecf20Sopenharmony_ci && (tps->chgconf & TPS_CHARGE_ENABLE) 3658c2ecf20Sopenharmony_ci ) { 3668c2ecf20Sopenharmony_ci if (tps->chgstatus & TPS_CHG_USB) { 3678c2ecf20Sopenharmony_ci /* VBUS options are readonly until reconnect */ 3688c2ecf20Sopenharmony_ci if (mask & TPS_CHG_USB) 3698c2ecf20Sopenharmony_ci set_bit(FLAG_VBUS_CHANGED, &tps->flags); 3708c2ecf20Sopenharmony_ci charging = 1; 3718c2ecf20Sopenharmony_ci } else if (tps->chgstatus & TPS_CHG_AC) 3728c2ecf20Sopenharmony_ci charging = 1; 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci if (charging != tps->charging) { 3758c2ecf20Sopenharmony_ci tps->charging = charging; 3768c2ecf20Sopenharmony_ci pr_info("%s: battery %scharging\n", 3778c2ecf20Sopenharmony_ci DRIVER_NAME, charging ? "" : 3788c2ecf20Sopenharmony_ci ((tps->chgstatus & (TPS_CHG_USB|TPS_CHG_AC)) 3798c2ecf20Sopenharmony_ci ? "NOT " : "dis")); 3808c2ecf20Sopenharmony_ci } 3818c2ecf20Sopenharmony_ci } 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci /* always poll to detect (a) power removal, without tps65013 3848c2ecf20Sopenharmony_ci * NO_CHG IRQ; or (b) restart of charging after stop. 3858c2ecf20Sopenharmony_ci */ 3868c2ecf20Sopenharmony_ci if ((tps->model != TPS65013 || !tps->charging) 3878c2ecf20Sopenharmony_ci && (tps->chgstatus & (TPS_CHG_USB|TPS_CHG_AC))) 3888c2ecf20Sopenharmony_ci poll = 1; 3898c2ecf20Sopenharmony_ci if (poll) 3908c2ecf20Sopenharmony_ci queue_delayed_work(system_power_efficient_wq, &tps->work, 3918c2ecf20Sopenharmony_ci POWER_POLL_DELAY); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci /* also potentially gpio-in rise or fall */ 3948c2ecf20Sopenharmony_ci} 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci/* handle IRQs and polling using keventd for now */ 3978c2ecf20Sopenharmony_cistatic void tps65010_work(struct work_struct *work) 3988c2ecf20Sopenharmony_ci{ 3998c2ecf20Sopenharmony_ci struct tps65010 *tps; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci tps = container_of(to_delayed_work(work), struct tps65010, work); 4028c2ecf20Sopenharmony_ci mutex_lock(&tps->lock); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci tps65010_interrupt(tps); 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci if (test_and_clear_bit(FLAG_VBUS_CHANGED, &tps->flags)) { 4078c2ecf20Sopenharmony_ci u8 chgconfig, tmp; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci chgconfig = i2c_smbus_read_byte_data(tps->client, 4108c2ecf20Sopenharmony_ci TPS_CHGCONFIG); 4118c2ecf20Sopenharmony_ci chgconfig &= ~(TPS_VBUS_500MA | TPS_VBUS_CHARGING); 4128c2ecf20Sopenharmony_ci if (tps->vbus == 500) 4138c2ecf20Sopenharmony_ci chgconfig |= TPS_VBUS_500MA | TPS_VBUS_CHARGING; 4148c2ecf20Sopenharmony_ci else if (tps->vbus >= 100) 4158c2ecf20Sopenharmony_ci chgconfig |= TPS_VBUS_CHARGING; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(tps->client, 4188c2ecf20Sopenharmony_ci TPS_CHGCONFIG, chgconfig); 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci /* vbus update fails unless VBUS is connected! */ 4218c2ecf20Sopenharmony_ci tmp = i2c_smbus_read_byte_data(tps->client, TPS_CHGCONFIG); 4228c2ecf20Sopenharmony_ci tps->chgconf = tmp; 4238c2ecf20Sopenharmony_ci show_chgconfig(tps->por, "update vbus", tmp); 4248c2ecf20Sopenharmony_ci } 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci if (test_and_clear_bit(FLAG_IRQ_ENABLE, &tps->flags)) 4278c2ecf20Sopenharmony_ci enable_irq(tps->client->irq); 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci mutex_unlock(&tps->lock); 4308c2ecf20Sopenharmony_ci} 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_cistatic irqreturn_t tps65010_irq(int irq, void *_tps) 4338c2ecf20Sopenharmony_ci{ 4348c2ecf20Sopenharmony_ci struct tps65010 *tps = _tps; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci disable_irq_nosync(irq); 4378c2ecf20Sopenharmony_ci set_bit(FLAG_IRQ_ENABLE, &tps->flags); 4388c2ecf20Sopenharmony_ci queue_delayed_work(system_power_efficient_wq, &tps->work, 0); 4398c2ecf20Sopenharmony_ci return IRQ_HANDLED; 4408c2ecf20Sopenharmony_ci} 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/ 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci/* offsets 0..3 == GPIO1..GPIO4 4458c2ecf20Sopenharmony_ci * offsets 4..5 == LED1/nPG, LED2 (we set one of the non-BLINK modes) 4468c2ecf20Sopenharmony_ci * offset 6 == vibrator motor driver 4478c2ecf20Sopenharmony_ci */ 4488c2ecf20Sopenharmony_cistatic void 4498c2ecf20Sopenharmony_citps65010_gpio_set(struct gpio_chip *chip, unsigned offset, int value) 4508c2ecf20Sopenharmony_ci{ 4518c2ecf20Sopenharmony_ci if (offset < 4) 4528c2ecf20Sopenharmony_ci tps65010_set_gpio_out_value(offset + 1, value); 4538c2ecf20Sopenharmony_ci else if (offset < 6) 4548c2ecf20Sopenharmony_ci tps65010_set_led(offset - 3, value ? ON : OFF); 4558c2ecf20Sopenharmony_ci else 4568c2ecf20Sopenharmony_ci tps65010_set_vib(value); 4578c2ecf20Sopenharmony_ci} 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_cistatic int 4608c2ecf20Sopenharmony_citps65010_output(struct gpio_chip *chip, unsigned offset, int value) 4618c2ecf20Sopenharmony_ci{ 4628c2ecf20Sopenharmony_ci /* GPIOs may be input-only */ 4638c2ecf20Sopenharmony_ci if (offset < 4) { 4648c2ecf20Sopenharmony_ci struct tps65010 *tps; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci tps = gpiochip_get_data(chip); 4678c2ecf20Sopenharmony_ci if (!(tps->outmask & (1 << offset))) 4688c2ecf20Sopenharmony_ci return -EINVAL; 4698c2ecf20Sopenharmony_ci tps65010_set_gpio_out_value(offset + 1, value); 4708c2ecf20Sopenharmony_ci } else if (offset < 6) 4718c2ecf20Sopenharmony_ci tps65010_set_led(offset - 3, value ? ON : OFF); 4728c2ecf20Sopenharmony_ci else 4738c2ecf20Sopenharmony_ci tps65010_set_vib(value); 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci return 0; 4768c2ecf20Sopenharmony_ci} 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_cistatic int tps65010_gpio_get(struct gpio_chip *chip, unsigned offset) 4798c2ecf20Sopenharmony_ci{ 4808c2ecf20Sopenharmony_ci int value; 4818c2ecf20Sopenharmony_ci struct tps65010 *tps; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci tps = gpiochip_get_data(chip); 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci if (offset < 4) { 4868c2ecf20Sopenharmony_ci value = i2c_smbus_read_byte_data(tps->client, TPS_DEFGPIO); 4878c2ecf20Sopenharmony_ci if (value < 0) 4888c2ecf20Sopenharmony_ci return value; 4898c2ecf20Sopenharmony_ci if (value & (1 << (offset + 4))) /* output */ 4908c2ecf20Sopenharmony_ci return !(value & (1 << offset)); 4918c2ecf20Sopenharmony_ci else /* input */ 4928c2ecf20Sopenharmony_ci return !!(value & (1 << offset)); 4938c2ecf20Sopenharmony_ci } 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci /* REVISIT we *could* report LED1/nPG and LED2 state ... */ 4968c2ecf20Sopenharmony_ci return 0; 4978c2ecf20Sopenharmony_ci} 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/ 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_cistatic struct tps65010 *the_tps; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_cistatic int tps65010_remove(struct i2c_client *client) 5058c2ecf20Sopenharmony_ci{ 5068c2ecf20Sopenharmony_ci struct tps65010 *tps = i2c_get_clientdata(client); 5078c2ecf20Sopenharmony_ci struct tps65010_board *board = dev_get_platdata(&client->dev); 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci if (board && board->teardown) { 5108c2ecf20Sopenharmony_ci int status = board->teardown(client, board->context); 5118c2ecf20Sopenharmony_ci if (status < 0) 5128c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "board %s %s err %d\n", 5138c2ecf20Sopenharmony_ci "teardown", client->name, status); 5148c2ecf20Sopenharmony_ci } 5158c2ecf20Sopenharmony_ci if (client->irq > 0) 5168c2ecf20Sopenharmony_ci free_irq(client->irq, tps); 5178c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&tps->work); 5188c2ecf20Sopenharmony_ci debugfs_remove(tps->file); 5198c2ecf20Sopenharmony_ci the_tps = NULL; 5208c2ecf20Sopenharmony_ci return 0; 5218c2ecf20Sopenharmony_ci} 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_cistatic int tps65010_probe(struct i2c_client *client, 5248c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 5258c2ecf20Sopenharmony_ci{ 5268c2ecf20Sopenharmony_ci struct tps65010 *tps; 5278c2ecf20Sopenharmony_ci int status; 5288c2ecf20Sopenharmony_ci struct tps65010_board *board = dev_get_platdata(&client->dev); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci if (the_tps) { 5318c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "only one tps6501x chip allowed\n"); 5328c2ecf20Sopenharmony_ci return -ENODEV; 5338c2ecf20Sopenharmony_ci } 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) 5368c2ecf20Sopenharmony_ci return -EINVAL; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci tps = devm_kzalloc(&client->dev, sizeof(*tps), GFP_KERNEL); 5398c2ecf20Sopenharmony_ci if (!tps) 5408c2ecf20Sopenharmony_ci return -ENOMEM; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci mutex_init(&tps->lock); 5438c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&tps->work, tps65010_work); 5448c2ecf20Sopenharmony_ci tps->client = client; 5458c2ecf20Sopenharmony_ci tps->model = id->driver_data; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci /* the IRQ is active low, but many gpio lines can't support that 5488c2ecf20Sopenharmony_ci * so this driver uses falling-edge triggers instead. 5498c2ecf20Sopenharmony_ci */ 5508c2ecf20Sopenharmony_ci if (client->irq > 0) { 5518c2ecf20Sopenharmony_ci status = request_irq(client->irq, tps65010_irq, 5528c2ecf20Sopenharmony_ci IRQF_TRIGGER_FALLING, DRIVER_NAME, tps); 5538c2ecf20Sopenharmony_ci if (status < 0) { 5548c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "can't get IRQ %d, err %d\n", 5558c2ecf20Sopenharmony_ci client->irq, status); 5568c2ecf20Sopenharmony_ci return status; 5578c2ecf20Sopenharmony_ci } 5588c2ecf20Sopenharmony_ci /* annoying race here, ideally we'd have an option 5598c2ecf20Sopenharmony_ci * to claim the irq now and enable it later. 5608c2ecf20Sopenharmony_ci * FIXME genirq IRQF_NOAUTOEN now solves that ... 5618c2ecf20Sopenharmony_ci */ 5628c2ecf20Sopenharmony_ci disable_irq(client->irq); 5638c2ecf20Sopenharmony_ci set_bit(FLAG_IRQ_ENABLE, &tps->flags); 5648c2ecf20Sopenharmony_ci } else 5658c2ecf20Sopenharmony_ci dev_warn(&client->dev, "IRQ not configured!\n"); 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci switch (tps->model) { 5698c2ecf20Sopenharmony_ci case TPS65010: 5708c2ecf20Sopenharmony_ci case TPS65012: 5718c2ecf20Sopenharmony_ci tps->por = 1; 5728c2ecf20Sopenharmony_ci break; 5738c2ecf20Sopenharmony_ci /* else CHGCONFIG.POR is replaced by AUA, enabling a WAIT mode */ 5748c2ecf20Sopenharmony_ci } 5758c2ecf20Sopenharmony_ci tps->chgconf = i2c_smbus_read_byte_data(client, TPS_CHGCONFIG); 5768c2ecf20Sopenharmony_ci show_chgconfig(tps->por, "conf/init", tps->chgconf); 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci show_chgstatus("chg/init", 5798c2ecf20Sopenharmony_ci i2c_smbus_read_byte_data(client, TPS_CHGSTATUS)); 5808c2ecf20Sopenharmony_ci show_regstatus("reg/init", 5818c2ecf20Sopenharmony_ci i2c_smbus_read_byte_data(client, TPS_REGSTATUS)); 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci pr_debug("%s: vdcdc1 0x%02x, vdcdc2 %02x, vregs1 %02x\n", DRIVER_NAME, 5848c2ecf20Sopenharmony_ci i2c_smbus_read_byte_data(client, TPS_VDCDC1), 5858c2ecf20Sopenharmony_ci i2c_smbus_read_byte_data(client, TPS_VDCDC2), 5868c2ecf20Sopenharmony_ci i2c_smbus_read_byte_data(client, TPS_VREGS1)); 5878c2ecf20Sopenharmony_ci pr_debug("%s: defgpio 0x%02x, mask3 0x%02x\n", DRIVER_NAME, 5888c2ecf20Sopenharmony_ci i2c_smbus_read_byte_data(client, TPS_DEFGPIO), 5898c2ecf20Sopenharmony_ci i2c_smbus_read_byte_data(client, TPS_MASK3)); 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci i2c_set_clientdata(client, tps); 5928c2ecf20Sopenharmony_ci the_tps = tps; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci#if defined(CONFIG_USB_GADGET) && !defined(CONFIG_USB_OTG) 5958c2ecf20Sopenharmony_ci /* USB hosts can't draw VBUS. OTG devices could, later 5968c2ecf20Sopenharmony_ci * when OTG infrastructure enables it. USB peripherals 5978c2ecf20Sopenharmony_ci * could be relying on VBUS while booting, though. 5988c2ecf20Sopenharmony_ci */ 5998c2ecf20Sopenharmony_ci tps->vbus = 100; 6008c2ecf20Sopenharmony_ci#endif 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci /* unmask the "interesting" irqs, then poll once to 6038c2ecf20Sopenharmony_ci * kickstart monitoring, initialize shadowed status 6048c2ecf20Sopenharmony_ci * registers, and maybe disable VBUS draw. 6058c2ecf20Sopenharmony_ci */ 6068c2ecf20Sopenharmony_ci tps->nmask1 = ~0; 6078c2ecf20Sopenharmony_ci (void) i2c_smbus_write_byte_data(client, TPS_MASK1, ~tps->nmask1); 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci tps->nmask2 = TPS_REG_ONOFF; 6108c2ecf20Sopenharmony_ci if (tps->model == TPS65013) 6118c2ecf20Sopenharmony_ci tps->nmask2 |= TPS_REG_NO_CHG; 6128c2ecf20Sopenharmony_ci (void) i2c_smbus_write_byte_data(client, TPS_MASK2, ~tps->nmask2); 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci (void) i2c_smbus_write_byte_data(client, TPS_MASK3, 0x0f 6158c2ecf20Sopenharmony_ci | i2c_smbus_read_byte_data(client, TPS_MASK3)); 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci tps65010_work(&tps->work.work); 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci tps->file = debugfs_create_file(DRIVER_NAME, S_IRUGO, NULL, 6208c2ecf20Sopenharmony_ci tps, DEBUG_FOPS); 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci /* optionally register GPIOs */ 6238c2ecf20Sopenharmony_ci if (board && board->base != 0) { 6248c2ecf20Sopenharmony_ci tps->outmask = board->outmask; 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci tps->chip.label = client->name; 6278c2ecf20Sopenharmony_ci tps->chip.parent = &client->dev; 6288c2ecf20Sopenharmony_ci tps->chip.owner = THIS_MODULE; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci tps->chip.set = tps65010_gpio_set; 6318c2ecf20Sopenharmony_ci tps->chip.direction_output = tps65010_output; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci /* NOTE: only partial support for inputs; nyet IRQs */ 6348c2ecf20Sopenharmony_ci tps->chip.get = tps65010_gpio_get; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci tps->chip.base = board->base; 6378c2ecf20Sopenharmony_ci tps->chip.ngpio = 7; 6388c2ecf20Sopenharmony_ci tps->chip.can_sleep = 1; 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci status = gpiochip_add_data(&tps->chip, tps); 6418c2ecf20Sopenharmony_ci if (status < 0) 6428c2ecf20Sopenharmony_ci dev_err(&client->dev, "can't add gpiochip, err %d\n", 6438c2ecf20Sopenharmony_ci status); 6448c2ecf20Sopenharmony_ci else if (board->setup) { 6458c2ecf20Sopenharmony_ci status = board->setup(client, board->context); 6468c2ecf20Sopenharmony_ci if (status < 0) { 6478c2ecf20Sopenharmony_ci dev_dbg(&client->dev, 6488c2ecf20Sopenharmony_ci "board %s %s err %d\n", 6498c2ecf20Sopenharmony_ci "setup", client->name, status); 6508c2ecf20Sopenharmony_ci status = 0; 6518c2ecf20Sopenharmony_ci } 6528c2ecf20Sopenharmony_ci } 6538c2ecf20Sopenharmony_ci } 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci return 0; 6568c2ecf20Sopenharmony_ci} 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_cistatic const struct i2c_device_id tps65010_id[] = { 6598c2ecf20Sopenharmony_ci { "tps65010", TPS65010 }, 6608c2ecf20Sopenharmony_ci { "tps65011", TPS65011 }, 6618c2ecf20Sopenharmony_ci { "tps65012", TPS65012 }, 6628c2ecf20Sopenharmony_ci { "tps65013", TPS65013 }, 6638c2ecf20Sopenharmony_ci { "tps65014", TPS65011 }, /* tps65011 charging at 6.5V max */ 6648c2ecf20Sopenharmony_ci { } 6658c2ecf20Sopenharmony_ci}; 6668c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, tps65010_id); 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_cistatic struct i2c_driver tps65010_driver = { 6698c2ecf20Sopenharmony_ci .driver = { 6708c2ecf20Sopenharmony_ci .name = "tps65010", 6718c2ecf20Sopenharmony_ci }, 6728c2ecf20Sopenharmony_ci .probe = tps65010_probe, 6738c2ecf20Sopenharmony_ci .remove = tps65010_remove, 6748c2ecf20Sopenharmony_ci .id_table = tps65010_id, 6758c2ecf20Sopenharmony_ci}; 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/ 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci/* Draw from VBUS: 6808c2ecf20Sopenharmony_ci * 0 mA -- DON'T DRAW (might supply power instead) 6818c2ecf20Sopenharmony_ci * 100 mA -- usb unit load (slowest charge rate) 6828c2ecf20Sopenharmony_ci * 500 mA -- usb high power (fast battery charge) 6838c2ecf20Sopenharmony_ci */ 6848c2ecf20Sopenharmony_ciint tps65010_set_vbus_draw(unsigned mA) 6858c2ecf20Sopenharmony_ci{ 6868c2ecf20Sopenharmony_ci unsigned long flags; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci if (!the_tps) 6898c2ecf20Sopenharmony_ci return -ENODEV; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci /* assumes non-SMP */ 6928c2ecf20Sopenharmony_ci local_irq_save(flags); 6938c2ecf20Sopenharmony_ci if (mA >= 500) 6948c2ecf20Sopenharmony_ci mA = 500; 6958c2ecf20Sopenharmony_ci else if (mA >= 100) 6968c2ecf20Sopenharmony_ci mA = 100; 6978c2ecf20Sopenharmony_ci else 6988c2ecf20Sopenharmony_ci mA = 0; 6998c2ecf20Sopenharmony_ci the_tps->vbus = mA; 7008c2ecf20Sopenharmony_ci if ((the_tps->chgstatus & TPS_CHG_USB) 7018c2ecf20Sopenharmony_ci && test_and_set_bit( 7028c2ecf20Sopenharmony_ci FLAG_VBUS_CHANGED, &the_tps->flags)) { 7038c2ecf20Sopenharmony_ci /* gadget drivers call this in_irq() */ 7048c2ecf20Sopenharmony_ci queue_delayed_work(system_power_efficient_wq, &the_tps->work, 7058c2ecf20Sopenharmony_ci 0); 7068c2ecf20Sopenharmony_ci } 7078c2ecf20Sopenharmony_ci local_irq_restore(flags); 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci return 0; 7108c2ecf20Sopenharmony_ci} 7118c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tps65010_set_vbus_draw); 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/ 7148c2ecf20Sopenharmony_ci/* tps65010_set_gpio_out_value parameter: 7158c2ecf20Sopenharmony_ci * gpio: GPIO1, GPIO2, GPIO3 or GPIO4 7168c2ecf20Sopenharmony_ci * value: LOW or HIGH 7178c2ecf20Sopenharmony_ci */ 7188c2ecf20Sopenharmony_ciint tps65010_set_gpio_out_value(unsigned gpio, unsigned value) 7198c2ecf20Sopenharmony_ci{ 7208c2ecf20Sopenharmony_ci int status; 7218c2ecf20Sopenharmony_ci unsigned defgpio; 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci if (!the_tps) 7248c2ecf20Sopenharmony_ci return -ENODEV; 7258c2ecf20Sopenharmony_ci if ((gpio < GPIO1) || (gpio > GPIO4)) 7268c2ecf20Sopenharmony_ci return -EINVAL; 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci mutex_lock(&the_tps->lock); 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci defgpio = i2c_smbus_read_byte_data(the_tps->client, TPS_DEFGPIO); 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci /* Configure GPIO for output */ 7338c2ecf20Sopenharmony_ci defgpio |= 1 << (gpio + 3); 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci /* Writing 1 forces a logic 0 on that GPIO and vice versa */ 7368c2ecf20Sopenharmony_ci switch (value) { 7378c2ecf20Sopenharmony_ci case LOW: 7388c2ecf20Sopenharmony_ci defgpio |= 1 << (gpio - 1); /* set GPIO low by writing 1 */ 7398c2ecf20Sopenharmony_ci break; 7408c2ecf20Sopenharmony_ci /* case HIGH: */ 7418c2ecf20Sopenharmony_ci default: 7428c2ecf20Sopenharmony_ci defgpio &= ~(1 << (gpio - 1)); /* set GPIO high by writing 0 */ 7438c2ecf20Sopenharmony_ci break; 7448c2ecf20Sopenharmony_ci } 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci status = i2c_smbus_write_byte_data(the_tps->client, 7478c2ecf20Sopenharmony_ci TPS_DEFGPIO, defgpio); 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci pr_debug("%s: gpio%dout = %s, defgpio 0x%02x\n", DRIVER_NAME, 7508c2ecf20Sopenharmony_ci gpio, value ? "high" : "low", 7518c2ecf20Sopenharmony_ci i2c_smbus_read_byte_data(the_tps->client, TPS_DEFGPIO)); 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci mutex_unlock(&the_tps->lock); 7548c2ecf20Sopenharmony_ci return status; 7558c2ecf20Sopenharmony_ci} 7568c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tps65010_set_gpio_out_value); 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/ 7598c2ecf20Sopenharmony_ci/* tps65010_set_led parameter: 7608c2ecf20Sopenharmony_ci * led: LED1 or LED2 7618c2ecf20Sopenharmony_ci * mode: ON, OFF or BLINK 7628c2ecf20Sopenharmony_ci */ 7638c2ecf20Sopenharmony_ciint tps65010_set_led(unsigned led, unsigned mode) 7648c2ecf20Sopenharmony_ci{ 7658c2ecf20Sopenharmony_ci int status; 7668c2ecf20Sopenharmony_ci unsigned led_on, led_per, offs; 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci if (!the_tps) 7698c2ecf20Sopenharmony_ci return -ENODEV; 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci if (led == LED1) 7728c2ecf20Sopenharmony_ci offs = 0; 7738c2ecf20Sopenharmony_ci else { 7748c2ecf20Sopenharmony_ci offs = 2; 7758c2ecf20Sopenharmony_ci led = LED2; 7768c2ecf20Sopenharmony_ci } 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci mutex_lock(&the_tps->lock); 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci pr_debug("%s: led%i_on 0x%02x\n", DRIVER_NAME, led, 7818c2ecf20Sopenharmony_ci i2c_smbus_read_byte_data(the_tps->client, 7828c2ecf20Sopenharmony_ci TPS_LED1_ON + offs)); 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci pr_debug("%s: led%i_per 0x%02x\n", DRIVER_NAME, led, 7858c2ecf20Sopenharmony_ci i2c_smbus_read_byte_data(the_tps->client, 7868c2ecf20Sopenharmony_ci TPS_LED1_PER + offs)); 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci switch (mode) { 7898c2ecf20Sopenharmony_ci case OFF: 7908c2ecf20Sopenharmony_ci led_on = 1 << 7; 7918c2ecf20Sopenharmony_ci led_per = 0 << 7; 7928c2ecf20Sopenharmony_ci break; 7938c2ecf20Sopenharmony_ci case ON: 7948c2ecf20Sopenharmony_ci led_on = 1 << 7; 7958c2ecf20Sopenharmony_ci led_per = 1 << 7; 7968c2ecf20Sopenharmony_ci break; 7978c2ecf20Sopenharmony_ci case BLINK: 7988c2ecf20Sopenharmony_ci led_on = 0x30 | (0 << 7); 7998c2ecf20Sopenharmony_ci led_per = 0x08 | (1 << 7); 8008c2ecf20Sopenharmony_ci break; 8018c2ecf20Sopenharmony_ci default: 8028c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: Wrong mode parameter for set_led()\n", 8038c2ecf20Sopenharmony_ci DRIVER_NAME); 8048c2ecf20Sopenharmony_ci mutex_unlock(&the_tps->lock); 8058c2ecf20Sopenharmony_ci return -EINVAL; 8068c2ecf20Sopenharmony_ci } 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci status = i2c_smbus_write_byte_data(the_tps->client, 8098c2ecf20Sopenharmony_ci TPS_LED1_ON + offs, led_on); 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci if (status != 0) { 8128c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: Failed to write led%i_on register\n", 8138c2ecf20Sopenharmony_ci DRIVER_NAME, led); 8148c2ecf20Sopenharmony_ci mutex_unlock(&the_tps->lock); 8158c2ecf20Sopenharmony_ci return status; 8168c2ecf20Sopenharmony_ci } 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci pr_debug("%s: led%i_on 0x%02x\n", DRIVER_NAME, led, 8198c2ecf20Sopenharmony_ci i2c_smbus_read_byte_data(the_tps->client, TPS_LED1_ON + offs)); 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci status = i2c_smbus_write_byte_data(the_tps->client, 8228c2ecf20Sopenharmony_ci TPS_LED1_PER + offs, led_per); 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci if (status != 0) { 8258c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: Failed to write led%i_per register\n", 8268c2ecf20Sopenharmony_ci DRIVER_NAME, led); 8278c2ecf20Sopenharmony_ci mutex_unlock(&the_tps->lock); 8288c2ecf20Sopenharmony_ci return status; 8298c2ecf20Sopenharmony_ci } 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci pr_debug("%s: led%i_per 0x%02x\n", DRIVER_NAME, led, 8328c2ecf20Sopenharmony_ci i2c_smbus_read_byte_data(the_tps->client, 8338c2ecf20Sopenharmony_ci TPS_LED1_PER + offs)); 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci mutex_unlock(&the_tps->lock); 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci return status; 8388c2ecf20Sopenharmony_ci} 8398c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tps65010_set_led); 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/ 8428c2ecf20Sopenharmony_ci/* tps65010_set_vib parameter: 8438c2ecf20Sopenharmony_ci * value: ON or OFF 8448c2ecf20Sopenharmony_ci */ 8458c2ecf20Sopenharmony_ciint tps65010_set_vib(unsigned value) 8468c2ecf20Sopenharmony_ci{ 8478c2ecf20Sopenharmony_ci int status; 8488c2ecf20Sopenharmony_ci unsigned vdcdc2; 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci if (!the_tps) 8518c2ecf20Sopenharmony_ci return -ENODEV; 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci mutex_lock(&the_tps->lock); 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci vdcdc2 = i2c_smbus_read_byte_data(the_tps->client, TPS_VDCDC2); 8568c2ecf20Sopenharmony_ci vdcdc2 &= ~(1 << 1); 8578c2ecf20Sopenharmony_ci if (value) 8588c2ecf20Sopenharmony_ci vdcdc2 |= (1 << 1); 8598c2ecf20Sopenharmony_ci status = i2c_smbus_write_byte_data(the_tps->client, 8608c2ecf20Sopenharmony_ci TPS_VDCDC2, vdcdc2); 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci pr_debug("%s: vibrator %s\n", DRIVER_NAME, value ? "on" : "off"); 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci mutex_unlock(&the_tps->lock); 8658c2ecf20Sopenharmony_ci return status; 8668c2ecf20Sopenharmony_ci} 8678c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tps65010_set_vib); 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/ 8708c2ecf20Sopenharmony_ci/* tps65010_set_low_pwr parameter: 8718c2ecf20Sopenharmony_ci * mode: ON or OFF 8728c2ecf20Sopenharmony_ci */ 8738c2ecf20Sopenharmony_ciint tps65010_set_low_pwr(unsigned mode) 8748c2ecf20Sopenharmony_ci{ 8758c2ecf20Sopenharmony_ci int status; 8768c2ecf20Sopenharmony_ci unsigned vdcdc1; 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci if (!the_tps) 8798c2ecf20Sopenharmony_ci return -ENODEV; 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci mutex_lock(&the_tps->lock); 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci pr_debug("%s: %s low_pwr, vdcdc1 0x%02x\n", DRIVER_NAME, 8848c2ecf20Sopenharmony_ci mode ? "enable" : "disable", 8858c2ecf20Sopenharmony_ci i2c_smbus_read_byte_data(the_tps->client, TPS_VDCDC1)); 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci vdcdc1 = i2c_smbus_read_byte_data(the_tps->client, TPS_VDCDC1); 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci switch (mode) { 8908c2ecf20Sopenharmony_ci case OFF: 8918c2ecf20Sopenharmony_ci vdcdc1 &= ~TPS_ENABLE_LP; /* disable ENABLE_LP bit */ 8928c2ecf20Sopenharmony_ci break; 8938c2ecf20Sopenharmony_ci /* case ON: */ 8948c2ecf20Sopenharmony_ci default: 8958c2ecf20Sopenharmony_ci vdcdc1 |= TPS_ENABLE_LP; /* enable ENABLE_LP bit */ 8968c2ecf20Sopenharmony_ci break; 8978c2ecf20Sopenharmony_ci } 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci status = i2c_smbus_write_byte_data(the_tps->client, 9008c2ecf20Sopenharmony_ci TPS_VDCDC1, vdcdc1); 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci if (status != 0) 9038c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: Failed to write vdcdc1 register\n", 9048c2ecf20Sopenharmony_ci DRIVER_NAME); 9058c2ecf20Sopenharmony_ci else 9068c2ecf20Sopenharmony_ci pr_debug("%s: vdcdc1 0x%02x\n", DRIVER_NAME, 9078c2ecf20Sopenharmony_ci i2c_smbus_read_byte_data(the_tps->client, TPS_VDCDC1)); 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci mutex_unlock(&the_tps->lock); 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci return status; 9128c2ecf20Sopenharmony_ci} 9138c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tps65010_set_low_pwr); 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/ 9168c2ecf20Sopenharmony_ci/* tps65010_config_vregs1 parameter: 9178c2ecf20Sopenharmony_ci * value to be written to VREGS1 register 9188c2ecf20Sopenharmony_ci * Note: The complete register is written, set all bits you need 9198c2ecf20Sopenharmony_ci */ 9208c2ecf20Sopenharmony_ciint tps65010_config_vregs1(unsigned value) 9218c2ecf20Sopenharmony_ci{ 9228c2ecf20Sopenharmony_ci int status; 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci if (!the_tps) 9258c2ecf20Sopenharmony_ci return -ENODEV; 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci mutex_lock(&the_tps->lock); 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci pr_debug("%s: vregs1 0x%02x\n", DRIVER_NAME, 9308c2ecf20Sopenharmony_ci i2c_smbus_read_byte_data(the_tps->client, TPS_VREGS1)); 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci status = i2c_smbus_write_byte_data(the_tps->client, 9338c2ecf20Sopenharmony_ci TPS_VREGS1, value); 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci if (status != 0) 9368c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: Failed to write vregs1 register\n", 9378c2ecf20Sopenharmony_ci DRIVER_NAME); 9388c2ecf20Sopenharmony_ci else 9398c2ecf20Sopenharmony_ci pr_debug("%s: vregs1 0x%02x\n", DRIVER_NAME, 9408c2ecf20Sopenharmony_ci i2c_smbus_read_byte_data(the_tps->client, TPS_VREGS1)); 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci mutex_unlock(&the_tps->lock); 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci return status; 9458c2ecf20Sopenharmony_ci} 9468c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tps65010_config_vregs1); 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ciint tps65010_config_vdcdc2(unsigned value) 9498c2ecf20Sopenharmony_ci{ 9508c2ecf20Sopenharmony_ci struct i2c_client *c; 9518c2ecf20Sopenharmony_ci int status; 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci if (!the_tps) 9548c2ecf20Sopenharmony_ci return -ENODEV; 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci c = the_tps->client; 9578c2ecf20Sopenharmony_ci mutex_lock(&the_tps->lock); 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci pr_debug("%s: vdcdc2 0x%02x\n", DRIVER_NAME, 9608c2ecf20Sopenharmony_ci i2c_smbus_read_byte_data(c, TPS_VDCDC2)); 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci status = i2c_smbus_write_byte_data(c, TPS_VDCDC2, value); 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci if (status != 0) 9658c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: Failed to write vdcdc2 register\n", 9668c2ecf20Sopenharmony_ci DRIVER_NAME); 9678c2ecf20Sopenharmony_ci else 9688c2ecf20Sopenharmony_ci pr_debug("%s: vregs1 0x%02x\n", DRIVER_NAME, 9698c2ecf20Sopenharmony_ci i2c_smbus_read_byte_data(c, TPS_VDCDC2)); 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci mutex_unlock(&the_tps->lock); 9728c2ecf20Sopenharmony_ci return status; 9738c2ecf20Sopenharmony_ci} 9748c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tps65010_config_vdcdc2); 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/ 9778c2ecf20Sopenharmony_ci/* tps65013_set_low_pwr parameter: 9788c2ecf20Sopenharmony_ci * mode: ON or OFF 9798c2ecf20Sopenharmony_ci */ 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci/* FIXME: Assumes AC or USB power is present. Setting AUA bit is not 9828c2ecf20Sopenharmony_ci required if power supply is through a battery */ 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ciint tps65013_set_low_pwr(unsigned mode) 9858c2ecf20Sopenharmony_ci{ 9868c2ecf20Sopenharmony_ci int status; 9878c2ecf20Sopenharmony_ci unsigned vdcdc1, chgconfig; 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci if (!the_tps || the_tps->por) 9908c2ecf20Sopenharmony_ci return -ENODEV; 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci mutex_lock(&the_tps->lock); 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci pr_debug("%s: %s low_pwr, chgconfig 0x%02x vdcdc1 0x%02x\n", 9958c2ecf20Sopenharmony_ci DRIVER_NAME, 9968c2ecf20Sopenharmony_ci mode ? "enable" : "disable", 9978c2ecf20Sopenharmony_ci i2c_smbus_read_byte_data(the_tps->client, TPS_CHGCONFIG), 9988c2ecf20Sopenharmony_ci i2c_smbus_read_byte_data(the_tps->client, TPS_VDCDC1)); 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci chgconfig = i2c_smbus_read_byte_data(the_tps->client, TPS_CHGCONFIG); 10018c2ecf20Sopenharmony_ci vdcdc1 = i2c_smbus_read_byte_data(the_tps->client, TPS_VDCDC1); 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci switch (mode) { 10048c2ecf20Sopenharmony_ci case OFF: 10058c2ecf20Sopenharmony_ci chgconfig &= ~TPS65013_AUA; /* disable AUA bit */ 10068c2ecf20Sopenharmony_ci vdcdc1 &= ~TPS_ENABLE_LP; /* disable ENABLE_LP bit */ 10078c2ecf20Sopenharmony_ci break; 10088c2ecf20Sopenharmony_ci /* case ON: */ 10098c2ecf20Sopenharmony_ci default: 10108c2ecf20Sopenharmony_ci chgconfig |= TPS65013_AUA; /* enable AUA bit */ 10118c2ecf20Sopenharmony_ci vdcdc1 |= TPS_ENABLE_LP; /* enable ENABLE_LP bit */ 10128c2ecf20Sopenharmony_ci break; 10138c2ecf20Sopenharmony_ci } 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci status = i2c_smbus_write_byte_data(the_tps->client, 10168c2ecf20Sopenharmony_ci TPS_CHGCONFIG, chgconfig); 10178c2ecf20Sopenharmony_ci if (status != 0) { 10188c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: Failed to write chconfig register\n", 10198c2ecf20Sopenharmony_ci DRIVER_NAME); 10208c2ecf20Sopenharmony_ci mutex_unlock(&the_tps->lock); 10218c2ecf20Sopenharmony_ci return status; 10228c2ecf20Sopenharmony_ci } 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci chgconfig = i2c_smbus_read_byte_data(the_tps->client, TPS_CHGCONFIG); 10258c2ecf20Sopenharmony_ci the_tps->chgconf = chgconfig; 10268c2ecf20Sopenharmony_ci show_chgconfig(0, "chgconf", chgconfig); 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci status = i2c_smbus_write_byte_data(the_tps->client, 10298c2ecf20Sopenharmony_ci TPS_VDCDC1, vdcdc1); 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci if (status != 0) 10328c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: Failed to write vdcdc1 register\n", 10338c2ecf20Sopenharmony_ci DRIVER_NAME); 10348c2ecf20Sopenharmony_ci else 10358c2ecf20Sopenharmony_ci pr_debug("%s: vdcdc1 0x%02x\n", DRIVER_NAME, 10368c2ecf20Sopenharmony_ci i2c_smbus_read_byte_data(the_tps->client, TPS_VDCDC1)); 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci mutex_unlock(&the_tps->lock); 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci return status; 10418c2ecf20Sopenharmony_ci} 10428c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tps65013_set_low_pwr); 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/ 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_cistatic int __init tps_init(void) 10478c2ecf20Sopenharmony_ci{ 10488c2ecf20Sopenharmony_ci return i2c_add_driver(&tps65010_driver); 10498c2ecf20Sopenharmony_ci} 10508c2ecf20Sopenharmony_ci/* NOTE: this MUST be initialized before the other parts of the system 10518c2ecf20Sopenharmony_ci * that rely on it ... but after the i2c bus on which this relies. 10528c2ecf20Sopenharmony_ci * That is, much earlier than on PC-type systems, which don't often use 10538c2ecf20Sopenharmony_ci * I2C as a core system bus. 10548c2ecf20Sopenharmony_ci */ 10558c2ecf20Sopenharmony_cisubsys_initcall(tps_init); 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_cistatic void __exit tps_exit(void) 10588c2ecf20Sopenharmony_ci{ 10598c2ecf20Sopenharmony_ci i2c_del_driver(&tps65010_driver); 10608c2ecf20Sopenharmony_ci} 10618c2ecf20Sopenharmony_cimodule_exit(tps_exit); 10628c2ecf20Sopenharmony_ci 1063