325 lines
8.2 KiB
C
325 lines
8.2 KiB
C
|
/* drivers/i2c/chips/tps65200.c
|
||
|
*
|
||
|
* Copyright (C) 2009 HTC Corporation
|
||
|
* Author: Josh Hsiao <Josh_Hsiao@htc.com>
|
||
|
*
|
||
|
* 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 <linux/module.h>
|
||
|
#include <linux/init.h>
|
||
|
#include <linux/slab.h>
|
||
|
#include <linux/i2c.h>
|
||
|
#include <linux/delay.h>
|
||
|
#include <mach/htc_battery.h>
|
||
|
#include <asm/mach-types.h>
|
||
|
|
||
|
static const unsigned short normal_i2c[] = { I2C_CLIENT_END };
|
||
|
|
||
|
/**
|
||
|
* Insmod parameters
|
||
|
*/
|
||
|
I2C_CLIENT_INSMOD_1(tps65200);
|
||
|
|
||
|
static int tps65200_probe(struct i2c_client *client,
|
||
|
const struct i2c_device_id *id);
|
||
|
static int tps65200_detect(struct i2c_client *client, int kind,
|
||
|
struct i2c_board_info *info);
|
||
|
static int tps65200_remove(struct i2c_client *client);
|
||
|
|
||
|
|
||
|
/* Supersonic for Switch charger */
|
||
|
struct tps65200_i2c_client {
|
||
|
struct i2c_client *client;
|
||
|
u8 address;
|
||
|
/* max numb of i2c_msg required is for read =2 */
|
||
|
struct i2c_msg xfer_msg[2];
|
||
|
/* To lock access to xfer_msg */
|
||
|
struct mutex xfer_lock;
|
||
|
};
|
||
|
static struct tps65200_i2c_client tps65200_i2c_module;
|
||
|
/**
|
||
|
Function:tps65200_i2c_write
|
||
|
Target: Write a byte to Switch charger
|
||
|
Timing: TBD
|
||
|
INPUT: value-> write value
|
||
|
reg -> reg offset
|
||
|
num-> number of byte to write
|
||
|
return :TRUE-->OK
|
||
|
FALSE-->Fail
|
||
|
*/
|
||
|
static int tps65200_i2c_write(u8 *value, u8 reg, u8 num_bytes)
|
||
|
{
|
||
|
int ret;
|
||
|
struct tps65200_i2c_client *tps;
|
||
|
struct i2c_msg *msg;
|
||
|
|
||
|
tps = &tps65200_i2c_module;
|
||
|
|
||
|
mutex_lock(&tps->xfer_lock);
|
||
|
/*
|
||
|
* [MSG1]: fill the register address data
|
||
|
* fill the data Tx buffer
|
||
|
*/
|
||
|
msg = &tps->xfer_msg[0];
|
||
|
msg->addr = tps->address;
|
||
|
msg->len = num_bytes + 1;
|
||
|
msg->flags = 0;
|
||
|
msg->buf = value;
|
||
|
/* over write the first byte of buffer with the register address */
|
||
|
*value = reg;
|
||
|
ret = i2c_transfer(tps->client->adapter, tps->xfer_msg, 1);
|
||
|
mutex_unlock(&tps->xfer_lock);
|
||
|
|
||
|
/* i2cTransfer returns num messages.translate it pls.. */
|
||
|
if (ret >= 0)
|
||
|
ret = 0;
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
Function:tps65200_i2c_read
|
||
|
Target: Read a byte from Switch charger
|
||
|
Timing: TBD
|
||
|
INPUT: value-> store buffer
|
||
|
reg -> reg offset to read
|
||
|
num-> number of byte to read
|
||
|
return :TRUE-->OK
|
||
|
FALSE-->Fail
|
||
|
*/
|
||
|
static int tps65200_i2c_read(u8 *value, u8 reg, u8 num_bytes)
|
||
|
{
|
||
|
int ret;
|
||
|
u8 val;
|
||
|
struct tps65200_i2c_client *tps;
|
||
|
struct i2c_msg *msg;
|
||
|
|
||
|
tps = &tps65200_i2c_module;
|
||
|
|
||
|
mutex_lock(&tps->xfer_lock);
|
||
|
/* [MSG1] fill the register address data */
|
||
|
msg = &tps->xfer_msg[0];
|
||
|
msg->addr = tps->address;
|
||
|
msg->len = 1;
|
||
|
msg->flags = 0; /* Read the register value */
|
||
|
val = reg;
|
||
|
msg->buf = &val;
|
||
|
/* [MSG2] fill the data rx buffer */
|
||
|
msg = &tps->xfer_msg[1];
|
||
|
msg->addr = tps->address;
|
||
|
msg->flags = I2C_M_RD; /* Read the register value */
|
||
|
msg->len = num_bytes; /* only n bytes */
|
||
|
msg->buf = value;
|
||
|
ret = i2c_transfer(tps->client->adapter, tps->xfer_msg, 2);
|
||
|
mutex_unlock(&tps->xfer_lock);
|
||
|
|
||
|
/* i2cTransfer returns num messages.translate it pls.. */
|
||
|
if (ret >= 0)
|
||
|
ret = 0;
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
Function:tps65200_i2c_write_byte
|
||
|
Target: Write a byte from Switch charger
|
||
|
Timing: TBD
|
||
|
INPUT: value-> store buffer
|
||
|
reg -> reg offset to read
|
||
|
return :TRUE-->OK
|
||
|
FALSE-->Fail
|
||
|
*/
|
||
|
static int tps65200_i2c_write_byte(u8 value, u8 reg)
|
||
|
{
|
||
|
/* 2 bytes offset 1 contains the data offset 0 is used by i2c_write */
|
||
|
int result;
|
||
|
u8 temp_buffer[2] = { 0 };
|
||
|
/* offset 1 contains the data */
|
||
|
temp_buffer[1] = value;
|
||
|
result = tps65200_i2c_write(temp_buffer, reg, 1);
|
||
|
if (result != 0)
|
||
|
pr_info("TPS65200 I2C write fail = %d\n", result);
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Function:tps65200_i2c_read_byte
|
||
|
Target: Read a byte from Switch charger
|
||
|
Timing: TBD
|
||
|
INPUT: value-> store buffer
|
||
|
reg -> reg offset to read
|
||
|
return :TRUE-->OK
|
||
|
FALSE-->Fail
|
||
|
*/
|
||
|
static int tps65200_i2c_read_byte(u8 *value, u8 reg)
|
||
|
{
|
||
|
int result = 0;
|
||
|
result = tps65200_i2c_read(value, reg, 1);
|
||
|
if (result != 0)
|
||
|
pr_info("TPS65200 I2C read fail = %d\n", result);
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
int tps_set_charger_ctrl(u32 ctl)
|
||
|
{
|
||
|
int result = 0;
|
||
|
u8 version;
|
||
|
u8 status;
|
||
|
u8 regh;
|
||
|
|
||
|
switch (ctl) {
|
||
|
case DISABLE:
|
||
|
pr_info("Switch charger OFF\n");
|
||
|
tps65200_i2c_write_byte(0x29, 0x01);
|
||
|
tps65200_i2c_write_byte(0x28, 0x00);
|
||
|
tps65200_i2c_read_byte(&status, 0x09);
|
||
|
pr_info("TPS65200 INT2 %x\n", status);
|
||
|
break;
|
||
|
case ENABLE_SLOW_CHG:
|
||
|
pr_info("Switch charger ON (SLOW)\n");
|
||
|
tps65200_i2c_write_byte(0x29, 0x01);
|
||
|
tps65200_i2c_write_byte(0x2A, 0x00);
|
||
|
tps65200_i2c_write_byte(0x86, 0x03);
|
||
|
break;
|
||
|
case ENABLE_FAST_CHG:
|
||
|
pr_info("Switch charger ON (FAST)\n");
|
||
|
tps65200_i2c_write_byte(0x29, 0x01);
|
||
|
tps65200_i2c_write_byte(0x2A, 0x00);
|
||
|
tps65200_i2c_write_byte(0x86, 0x03);
|
||
|
tps65200_i2c_write_byte(0xA3, 0x02);
|
||
|
tps65200_i2c_read_byte(®h, 0x01);
|
||
|
pr_info("1.batt: Switch charger ON (FAST): regh 0x01=%x\n", regh);
|
||
|
tps65200_i2c_read_byte(®h, 0x00);
|
||
|
pr_info("2.batt: Switch charger ON (FAST): regh 0x00=%x\n", regh);
|
||
|
tps65200_i2c_read_byte(®h, 0x03);
|
||
|
pr_info("2.batt: Switch charger ON (FAST): regh 0x03=%x\n", regh);
|
||
|
tps65200_i2c_read_byte(®h, 0x02);
|
||
|
pr_info("2.batt: Switch charger ON (FAST): regh 0x02=%x\n", regh);
|
||
|
break;
|
||
|
case CHECK_CHG:
|
||
|
pr_info("Switch charger CHECK \n");
|
||
|
tps65200_i2c_read_byte(&status, 0x06);
|
||
|
pr_info("TPS65200 STATUS_A%x\n", status);
|
||
|
break;
|
||
|
case SET_ICL500:
|
||
|
pr_info("Switch charger SET_ICL500 \n");
|
||
|
tps65200_i2c_write_byte(0xA3, 0x02);
|
||
|
break;
|
||
|
case SET_ICL100:
|
||
|
pr_info("Switch charger SET_ICL100 \n");
|
||
|
tps65200_i2c_write_byte(0x23, 0x02);
|
||
|
break;
|
||
|
case CHECK_INT2:
|
||
|
pr_info("Switch charger CHECK_INT2 \n");
|
||
|
tps65200_i2c_read_byte(&status, 0x09);
|
||
|
pr_info("TPS65200 INT2 %x\n", status);
|
||
|
break;
|
||
|
default:
|
||
|
pr_info("%s: Not supported battery ctr called.!", __func__);
|
||
|
result = -EINVAL;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
EXPORT_SYMBOL(tps_set_charger_ctrl);
|
||
|
static int cable_status_handler_func(struct notifier_block *nfb,
|
||
|
unsigned long action, void *param)
|
||
|
{
|
||
|
u32 ctl = (u32)action;
|
||
|
pr_info("TPS65200 Switch charger set control%d\n", ctl);
|
||
|
tps_set_charger_ctrl(ctl);
|
||
|
|
||
|
return NOTIFY_OK;
|
||
|
}
|
||
|
|
||
|
static struct notifier_block cable_status_handler = {
|
||
|
.notifier_call = cable_status_handler_func,
|
||
|
};
|
||
|
|
||
|
static int tps65200_detect(struct i2c_client *client, int kind,
|
||
|
struct i2c_board_info *info)
|
||
|
{
|
||
|
if (!i2c_check_functionality(client->adapter,
|
||
|
I2C_FUNC_SMBUS_WRITE_BYTE_DATA |
|
||
|
I2C_FUNC_SMBUS_BYTE))
|
||
|
return -ENODEV;
|
||
|
|
||
|
strlcpy(info->type, "tps65200", I2C_NAME_SIZE);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int tps65200_probe(struct i2c_client *client,
|
||
|
const struct i2c_device_id *id)
|
||
|
{
|
||
|
struct tps65200_i2c_client *data = &tps65200_i2c_module;
|
||
|
|
||
|
if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C) == 0) {
|
||
|
dev_dbg(&client->dev, "[TPS65200]:I2C fail\n");
|
||
|
return -EIO;
|
||
|
}
|
||
|
|
||
|
register_notifier_cable_status(&cable_status_handler);
|
||
|
|
||
|
data->address = client->addr;
|
||
|
data->client = client;
|
||
|
mutex_init(&data->xfer_lock);
|
||
|
pr_info("[TPS65200]: Driver registration done\n");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int tps65200_remove(struct i2c_client *client)
|
||
|
{
|
||
|
struct tps65200_i2c_client *data = i2c_get_clientdata(client);
|
||
|
int idx;
|
||
|
if (data->client && data->client != client)
|
||
|
i2c_unregister_device(data->client);
|
||
|
tps65200_i2c_module.client = NULL;
|
||
|
return 0;
|
||
|
}
|
||
|
static const struct i2c_device_id tps65200_id[] = {
|
||
|
{ "tps65200", 0 },
|
||
|
{ },
|
||
|
};
|
||
|
static struct i2c_driver tps65200_driver = {
|
||
|
.driver.name = "tps65200",
|
||
|
.id_table = tps65200_id,
|
||
|
.probe = tps65200_probe,
|
||
|
.remove = tps65200_remove,
|
||
|
};
|
||
|
|
||
|
static int __init sensors_tps65200_init(void)
|
||
|
{
|
||
|
int res;
|
||
|
|
||
|
res = i2c_add_driver(&tps65200_driver);
|
||
|
if (res) {
|
||
|
pr_info("[TPS65200]: Driver registration failed \n");
|
||
|
return res;
|
||
|
}
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
static void __exit sensors_tps65200_exit(void)
|
||
|
{
|
||
|
i2c_del_driver(&tps65200_driver);
|
||
|
}
|
||
|
|
||
|
MODULE_AUTHOR("Josh Hsiao <Josh_Hsiao@htc.com>");
|
||
|
MODULE_DESCRIPTION("tps65200 driver");
|
||
|
MODULE_LICENSE("GPL");
|
||
|
|
||
|
module_init(sensors_tps65200_init);
|
||
|
module_exit(sensors_tps65200_exit);
|