]>
Commit | Line | Data |
---|---|---|
798a8eee LW |
1 | /* |
2 | * Core driver for TPS61050/61052 boost converters, used for while LED | |
3 | * driving, audio power amplification, white LED flash, and generic | |
4 | * boost conversion. Additionally it provides a 1-bit GPIO pin (out or in) | |
5 | * and a flash synchronization pin to synchronize flash events when used as | |
6 | * flashgun. | |
7 | * | |
8 | * Copyright (C) 2011 ST-Ericsson SA | |
9 | * Written on behalf of Linaro for ST-Ericsson | |
10 | * | |
11 | * Author: Linus Walleij <[email protected]> | |
12 | * | |
13 | * License terms: GNU General Public License (GPL) version 2 | |
14 | */ | |
15 | ||
16 | #include <linux/module.h> | |
17 | #include <linux/init.h> | |
18 | #include <linux/i2c.h> | |
19 | #include <linux/mutex.h> | |
20 | #include <linux/gpio.h> | |
21 | #include <linux/spinlock.h> | |
22 | #include <linux/slab.h> | |
23 | #include <linux/err.h> | |
24 | #include <linux/regulator/driver.h> | |
25 | #include <linux/mfd/core.h> | |
26 | #include <linux/mfd/tps6105x.h> | |
27 | ||
28 | int tps6105x_set(struct tps6105x *tps6105x, u8 reg, u8 value) | |
29 | { | |
30 | int ret; | |
31 | ||
32 | ret = mutex_lock_interruptible(&tps6105x->lock); | |
33 | if (ret) | |
34 | return ret; | |
35 | ret = i2c_smbus_write_byte_data(tps6105x->client, reg, value); | |
36 | mutex_unlock(&tps6105x->lock); | |
37 | if (ret < 0) | |
38 | return ret; | |
39 | ||
40 | return 0; | |
41 | } | |
42 | EXPORT_SYMBOL(tps6105x_set); | |
43 | ||
44 | int tps6105x_get(struct tps6105x *tps6105x, u8 reg, u8 *buf) | |
45 | { | |
46 | int ret; | |
47 | ||
48 | ret = mutex_lock_interruptible(&tps6105x->lock); | |
49 | if (ret) | |
50 | return ret; | |
51 | ret = i2c_smbus_read_byte_data(tps6105x->client, reg); | |
52 | mutex_unlock(&tps6105x->lock); | |
53 | if (ret < 0) | |
54 | return ret; | |
55 | ||
56 | *buf = ret; | |
57 | return 0; | |
58 | } | |
59 | EXPORT_SYMBOL(tps6105x_get); | |
60 | ||
61 | /* | |
62 | * Masks off the bits in the mask and sets the bits in the bitvalues | |
63 | * parameter in one atomic operation | |
64 | */ | |
65 | int tps6105x_mask_and_set(struct tps6105x *tps6105x, u8 reg, | |
66 | u8 bitmask, u8 bitvalues) | |
67 | { | |
68 | int ret; | |
69 | u8 regval; | |
70 | ||
71 | ret = mutex_lock_interruptible(&tps6105x->lock); | |
72 | if (ret) | |
73 | return ret; | |
74 | ret = i2c_smbus_read_byte_data(tps6105x->client, reg); | |
75 | if (ret < 0) | |
76 | goto fail; | |
77 | regval = ret; | |
78 | regval = (~bitmask & regval) | (bitmask & bitvalues); | |
79 | ret = i2c_smbus_write_byte_data(tps6105x->client, reg, regval); | |
80 | fail: | |
81 | mutex_unlock(&tps6105x->lock); | |
82 | if (ret < 0) | |
83 | return ret; | |
84 | ||
85 | return 0; | |
86 | } | |
87 | EXPORT_SYMBOL(tps6105x_mask_and_set); | |
88 | ||
89 | static int __devinit tps6105x_startup(struct tps6105x *tps6105x) | |
90 | { | |
91 | int ret; | |
92 | u8 regval; | |
93 | ||
94 | ret = tps6105x_get(tps6105x, TPS6105X_REG_0, ®val); | |
95 | if (ret) | |
96 | return ret; | |
97 | switch (regval >> TPS6105X_REG0_MODE_SHIFT) { | |
98 | case TPS6105X_REG0_MODE_SHUTDOWN: | |
99 | dev_info(&tps6105x->client->dev, | |
100 | "TPS6105x found in SHUTDOWN mode\n"); | |
101 | break; | |
102 | case TPS6105X_REG0_MODE_TORCH: | |
103 | dev_info(&tps6105x->client->dev, | |
104 | "TPS6105x found in TORCH mode\n"); | |
105 | break; | |
106 | case TPS6105X_REG0_MODE_TORCH_FLASH: | |
107 | dev_info(&tps6105x->client->dev, | |
108 | "TPS6105x found in FLASH mode\n"); | |
109 | break; | |
110 | case TPS6105X_REG0_MODE_VOLTAGE: | |
111 | dev_info(&tps6105x->client->dev, | |
112 | "TPS6105x found in VOLTAGE mode\n"); | |
113 | break; | |
114 | default: | |
115 | break; | |
116 | } | |
117 | ||
118 | return ret; | |
119 | } | |
120 | ||
121 | /* | |
122 | * MFD cells - we have one cell which is selected operation | |
123 | * mode, and we always have a GPIO cell. | |
124 | */ | |
125 | static struct mfd_cell tps6105x_cells[] = { | |
126 | { | |
127 | /* name will be runtime assigned */ | |
128 | .id = -1, | |
129 | }, | |
130 | { | |
131 | .name = "tps6105x-gpio", | |
132 | .id = -1, | |
133 | }, | |
134 | }; | |
135 | ||
136 | static int __devinit tps6105x_probe(struct i2c_client *client, | |
137 | const struct i2c_device_id *id) | |
138 | { | |
139 | struct tps6105x *tps6105x; | |
140 | struct tps6105x_platform_data *pdata; | |
141 | int ret; | |
142 | int i; | |
143 | ||
144 | tps6105x = kmalloc(sizeof(*tps6105x), GFP_KERNEL); | |
145 | if (!tps6105x) | |
146 | return -ENOMEM; | |
147 | ||
148 | i2c_set_clientdata(client, tps6105x); | |
149 | tps6105x->client = client; | |
150 | pdata = client->dev.platform_data; | |
151 | tps6105x->pdata = pdata; | |
152 | mutex_init(&tps6105x->lock); | |
153 | ||
154 | ret = tps6105x_startup(tps6105x); | |
155 | if (ret) { | |
156 | dev_err(&client->dev, "chip initialization failed\n"); | |
157 | goto fail; | |
158 | } | |
159 | ||
160 | /* Remove warning texts when you implement new cell drivers */ | |
161 | switch (pdata->mode) { | |
162 | case TPS6105X_MODE_SHUTDOWN: | |
163 | dev_info(&client->dev, | |
164 | "present, not used for anything, only GPIO\n"); | |
165 | break; | |
166 | case TPS6105X_MODE_TORCH: | |
167 | tps6105x_cells[0].name = "tps6105x-leds"; | |
168 | dev_warn(&client->dev, | |
169 | "torch mode is unsupported\n"); | |
170 | break; | |
171 | case TPS6105X_MODE_TORCH_FLASH: | |
172 | tps6105x_cells[0].name = "tps6105x-flash"; | |
173 | dev_warn(&client->dev, | |
174 | "flash mode is unsupported\n"); | |
175 | break; | |
176 | case TPS6105X_MODE_VOLTAGE: | |
177 | tps6105x_cells[0].name ="tps6105x-regulator"; | |
178 | break; | |
179 | default: | |
180 | break; | |
181 | } | |
182 | ||
183 | /* Set up and register the platform devices. */ | |
184 | for (i = 0; i < ARRAY_SIZE(tps6105x_cells); i++) { | |
185 | /* One state holder for all drivers, this is simple */ | |
a7c98ce2 SO |
186 | tps6105x_cells[i].platform_data = tps6105x; |
187 | tps6105x_cells[i].pdata_size = sizeof(*tps6105x); | |
798a8eee LW |
188 | } |
189 | ||
190 | ret = mfd_add_devices(&client->dev, 0, tps6105x_cells, | |
191 | ARRAY_SIZE(tps6105x_cells), NULL, 0); | |
192 | if (ret) | |
193 | goto fail; | |
194 | ||
195 | return 0; | |
196 | ||
197 | fail: | |
198 | kfree(tps6105x); | |
199 | return ret; | |
200 | } | |
201 | ||
202 | static int __devexit tps6105x_remove(struct i2c_client *client) | |
203 | { | |
204 | struct tps6105x *tps6105x = i2c_get_clientdata(client); | |
205 | ||
206 | mfd_remove_devices(&client->dev); | |
207 | ||
208 | /* Put chip in shutdown mode */ | |
209 | tps6105x_mask_and_set(tps6105x, TPS6105X_REG_0, | |
210 | TPS6105X_REG0_MODE_MASK, | |
211 | TPS6105X_MODE_SHUTDOWN << TPS6105X_REG0_MODE_SHIFT); | |
212 | ||
213 | kfree(tps6105x); | |
214 | return 0; | |
215 | } | |
216 | ||
217 | static const struct i2c_device_id tps6105x_id[] = { | |
218 | { "tps61050", 0 }, | |
219 | { "tps61052", 0 }, | |
220 | { } | |
221 | }; | |
222 | MODULE_DEVICE_TABLE(i2c, tps6105x_id); | |
223 | ||
224 | static struct i2c_driver tps6105x_driver = { | |
225 | .driver = { | |
226 | .name = "tps6105x", | |
227 | }, | |
228 | .probe = tps6105x_probe, | |
229 | .remove = __devexit_p(tps6105x_remove), | |
230 | .id_table = tps6105x_id, | |
231 | }; | |
232 | ||
233 | static int __init tps6105x_init(void) | |
234 | { | |
235 | return i2c_add_driver(&tps6105x_driver); | |
236 | } | |
237 | subsys_initcall(tps6105x_init); | |
238 | ||
239 | static void __exit tps6105x_exit(void) | |
240 | { | |
241 | i2c_del_driver(&tps6105x_driver); | |
242 | } | |
243 | module_exit(tps6105x_exit); | |
244 | ||
245 | MODULE_AUTHOR("Linus Walleij"); | |
246 | MODULE_DESCRIPTION("TPS6105x White LED Boost Converter Driver"); | |
247 | MODULE_LICENSE("GPL v2"); |