blob: b80db78491e190c1c95c524043d3e047c0fffc65 [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* drivers/i2c/chips/smb329.c
2 *
3 * SMB329B Switch Charger (SUMMIT Microelectronics)
4 *
5 * Copyright (C) 2009 HTC Corporation
6 * Author: Justin Lin <Justin_Lin@htc.com>
7 *
8 * This software is licensed under the terms of the GNU General Public
9 * License version 2, as published by the Free Software Foundation, and
10 * may be copied, distributed, and modified under those terms.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 */
18
19#include <linux/module.h>
20#include <linux/init.h>
21#include <linux/slab.h>
22#include <linux/i2c.h>
23#include <linux/delay.h>
24#include <linux/workqueue.h>
25#include <linux/mutex.h>
26#include <asm/atomic.h>
27
28#include "board-mahimahi-smb329.h"
29
30static struct smb329_data {
31 struct i2c_client *client;
32 uint8_t version;
33 struct work_struct work;
34 struct mutex state_lock;
35 int chg_state;
36} smb329;
37
38static int smb329_i2c_write(uint8_t *value, uint8_t reg, uint8_t num_bytes)
39{
40 int ret;
41 struct i2c_msg msg;
42
43 /* write the first byte of buffer as the register address */
44 value[0] = reg;
45 msg.addr = smb329.client->addr;
46 msg.len = num_bytes + 1;
47 msg.flags = 0;
48 msg.buf = value;
49
50 ret = i2c_transfer(smb329.client->adapter, &msg, 1);
51
52 return (ret >= 0) ? 0 : ret;
53}
54
55static int smb329_i2c_read(uint8_t *value, uint8_t reg, uint8_t num_bytes)
56{
57 int ret;
58 struct i2c_msg msg[2];
59
60 /* setup the address to read */
61 msg[0].addr = smb329.client->addr;
62 msg[0].len = 1;
63 msg[0].flags = 0;
64 msg[0].buf = &reg;
65
66 /* setup the read buffer */
67 msg[1].addr = smb329.client->addr;
68 msg[1].flags = I2C_M_RD;
69 msg[1].len = num_bytes;
70 msg[1].buf = value;
71
72 ret = i2c_transfer(smb329.client->adapter, msg, 2);
73
74 return (ret >= 0) ? 0 : ret;
75}
76
77static int smb329_i2c_write_byte(uint8_t value, uint8_t reg)
78{
79 int ret;
80 uint8_t buf[2] = { 0 };
81
82 buf[1] = value;
83 ret = smb329_i2c_write(buf, reg, 1);
84 if (ret)
85 pr_err("smb329: write byte error (%d)\n", ret);
86
87 return ret;
88}
89
90static int smb329_i2c_read_byte(uint8_t *value, uint8_t reg)
91{
92 int ret = smb329_i2c_read(value, reg, 1);
93 if (ret)
94 pr_err("smb329: read byte error (%d)\n", ret);
95
96 return ret;
97}
98
99int smb329_set_charger_ctrl(uint32_t ctl)
100{
101 mutex_lock(&smb329.state_lock);
102 smb329.chg_state = ctl;
103 schedule_work(&smb329.work);
104 mutex_unlock(&smb329.state_lock);
105 return 0;
106}
107
108static void smb329_work_func(struct work_struct *work)
109{
110 mutex_lock(&smb329.state_lock);
111
112 switch (smb329.chg_state) {
113 case SMB329_ENABLE_FAST_CHG:
114 pr_info("smb329: charger on (fast)\n");
115 smb329_i2c_write_byte(0x84, 0x31);
116 smb329_i2c_write_byte(0x08, 0x05);
117 if ((smb329.version & 0x18) == 0x0)
118 smb329_i2c_write_byte(0xA9, 0x00);
119 break;
120
121 case SMB329_DISABLE_CHG:
122 case SMB329_ENABLE_SLOW_CHG:
123 pr_info("smb329: charger off/slow\n");
124 smb329_i2c_write_byte(0x88, 0x31);
125 smb329_i2c_write_byte(0x08, 0x05);
126 break;
127 default:
128 pr_err("smb329: unknown charger state %d\n",
129 smb329.chg_state);
130 }
131
132 mutex_unlock(&smb329.state_lock);
133}
134
135static int smb329_probe(struct i2c_client *client,
136 const struct i2c_device_id *id)
137{
138 if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C) == 0) {
139 dev_dbg(&client->dev, "[SMB329]:I2C fail\n");
140 return -EIO;
141 }
142
143 smb329.client = client;
144 mutex_init(&smb329.state_lock);
145 INIT_WORK(&smb329.work, smb329_work_func);
146
147 smb329_i2c_read_byte(&smb329.version, 0x3B);
148 pr_info("smb329 version: 0x%02x\n", smb329.version);
149
150 return 0;
151}
152
153static const struct i2c_device_id smb329_id[] = {
154 { "smb329", 0 },
155 { },
156};
157
158static struct i2c_driver smb329_driver = {
159 .driver.name = "smb329",
160 .id_table = smb329_id,
161 .probe = smb329_probe,
162};
163
164static int __init smb329_init(void)
165{
166 int ret = i2c_add_driver(&smb329_driver);
167 if (ret)
168 pr_err("smb329_init: failed\n");
169
170 return ret;
171}
172
173module_init(smb329_init);
174
175MODULE_AUTHOR("Justin Lin <Justin_Lin@htc.com>");
176MODULE_DESCRIPTION("SUMMIT Microelectronics SMB329B switch charger");
177MODULE_LICENSE("GPL");