/* drivers/i2c/chips/smb329.c * * SMB329B Switch Charger (SUMMIT Microelectronics) * * Copyright (C) 2009 HTC Corporation * Author: Justin Lin * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #include #include #include #include #include #include #include #include #include "board-mahimahi-smb329.h" static struct smb329_data { struct i2c_client *client; uint8_t version; struct work_struct work; struct mutex state_lock; int chg_state; } smb329; static int smb329_i2c_write(uint8_t *value, uint8_t reg, uint8_t num_bytes) { int ret; struct i2c_msg msg; /* write the first byte of buffer as the register address */ value[0] = reg; msg.addr = smb329.client->addr; msg.len = num_bytes + 1; msg.flags = 0; msg.buf = value; ret = i2c_transfer(smb329.client->adapter, &msg, 1); return (ret >= 0) ? 0 : ret; } static int smb329_i2c_read(uint8_t *value, uint8_t reg, uint8_t num_bytes) { int ret; struct i2c_msg msg[2]; /* setup the address to read */ msg[0].addr = smb329.client->addr; msg[0].len = 1; msg[0].flags = 0; msg[0].buf = ® /* setup the read buffer */ msg[1].addr = smb329.client->addr; msg[1].flags = I2C_M_RD; msg[1].len = num_bytes; msg[1].buf = value; ret = i2c_transfer(smb329.client->adapter, msg, 2); return (ret >= 0) ? 0 : ret; } static int smb329_i2c_write_byte(uint8_t value, uint8_t reg) { int ret; uint8_t buf[2] = { 0 }; buf[1] = value; ret = smb329_i2c_write(buf, reg, 1); if (ret) pr_err("smb329: write byte error (%d)\n", ret); return ret; } static int smb329_i2c_read_byte(uint8_t *value, uint8_t reg) { int ret = smb329_i2c_read(value, reg, 1); if (ret) pr_err("smb329: read byte error (%d)\n", ret); return ret; } int smb329_set_charger_ctrl(uint32_t ctl) { mutex_lock(&smb329.state_lock); smb329.chg_state = ctl; schedule_work(&smb329.work); mutex_unlock(&smb329.state_lock); return 0; } static void smb329_work_func(struct work_struct *work) { mutex_lock(&smb329.state_lock); switch (smb329.chg_state) { case SMB329_ENABLE_FAST_CHG: pr_info("smb329: charger on (fast)\n"); smb329_i2c_write_byte(0x84, 0x31); smb329_i2c_write_byte(0x08, 0x05); if ((smb329.version & 0x18) == 0x0) smb329_i2c_write_byte(0xA9, 0x00); break; case SMB329_DISABLE_CHG: case SMB329_ENABLE_SLOW_CHG: pr_info("smb329: charger off/slow\n"); smb329_i2c_write_byte(0x88, 0x31); smb329_i2c_write_byte(0x08, 0x05); break; default: pr_err("smb329: unknown charger state %d\n", smb329.chg_state); } mutex_unlock(&smb329.state_lock); } static int smb329_probe(struct i2c_client *client, const struct i2c_device_id *id) { if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C) == 0) { dev_dbg(&client->dev, "[SMB329]:I2C fail\n"); return -EIO; } smb329.client = client; mutex_init(&smb329.state_lock); INIT_WORK(&smb329.work, smb329_work_func); smb329_i2c_read_byte(&smb329.version, 0x3B); pr_info("smb329 version: 0x%02x\n", smb329.version); return 0; } static const struct i2c_device_id smb329_id[] = { { "smb329", 0 }, { }, }; static struct i2c_driver smb329_driver = { .driver.name = "smb329", .id_table = smb329_id, .probe = smb329_probe, }; static int __init smb329_init(void) { int ret = i2c_add_driver(&smb329_driver); if (ret) pr_err("smb329_init: failed\n"); return ret; } module_init(smb329_init); MODULE_AUTHOR("Justin Lin "); MODULE_DESCRIPTION("SUMMIT Microelectronics SMB329B switch charger"); MODULE_LICENSE("GPL");