[RAMEN9610-20877]p54usb: Fix race between disconnect and firmware loading
[GitHub/moto-9609/android_kernel_motorola_exynos9610.git] / drivers / mfd / s2mpb02-core.c
1 /*
2 * s2mpb02-core.c - mfd core driver for the Samsung s2mpb02
3 *
4 * Copyright (C) 2014 Samsung Electronics
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 *
20 * This driver is based on max77804.c
21 */
22
23 #include <linux/module.h>
24 #include <linux/slab.h>
25 #include <linux/i2c.h>
26 #include <linux/irq.h>
27 #include <linux/interrupt.h>
28 #include <linux/mutex.h>
29 #include <linux/mfd/core.h>
30 #include <linux/mfd/s2mpb02.h>
31 #include <linux/mfd/s2mpb02-regulator.h>
32 #include <linux/regulator/machine.h>
33
34 #ifdef CONFIG_OF
35 #include <linux/of_device.h>
36 #include <linux/of_gpio.h>
37 #endif /* CONFIG_OF */
38
39 static struct mfd_cell s2mpb02_devs[] = {
40 #ifdef CONFIG_LEDS_S2MPB02
41 { .name = "s2mpb02-led", },
42 #endif /* CONFIG_LEDS_S2MPB02 */
43 { .name = "s2mpb02-regulator", },
44 };
45
46 int s2mpb02_read_reg(struct i2c_client *i2c, u8 reg, u8 *dest)
47 {
48 struct s2mpb02_dev *s2mpb02 = i2c_get_clientdata(i2c);
49 int ret;
50
51 mutex_lock(&s2mpb02->i2c_lock);
52 ret = i2c_smbus_read_byte_data(i2c, reg);
53 mutex_unlock(&s2mpb02->i2c_lock);
54 if (ret < 0) {
55 pr_info("%s:%s reg(0x%x), ret(%d)\n",
56 MFD_DEV_NAME, __func__, reg, ret);
57 return ret;
58 }
59
60 ret &= 0xff;
61 *dest = ret;
62 return 0;
63 }
64 EXPORT_SYMBOL_GPL(s2mpb02_read_reg);
65
66 int s2mpb02_bulk_read(struct i2c_client *i2c, u8 reg, int count, u8 *buf)
67 {
68 struct s2mpb02_dev *s2mpb02 = i2c_get_clientdata(i2c);
69 int ret;
70
71 mutex_lock(&s2mpb02->i2c_lock);
72 ret = i2c_smbus_read_i2c_block_data(i2c, reg, count, buf);
73 mutex_unlock(&s2mpb02->i2c_lock);
74 if (ret < 0)
75 return ret;
76
77 return 0;
78 }
79 EXPORT_SYMBOL_GPL(s2mpb02_bulk_read);
80
81 int s2mpb02_write_reg(struct i2c_client *i2c, u8 reg, u8 value)
82 {
83 struct s2mpb02_dev *s2mpb02 = i2c_get_clientdata(i2c);
84 int ret;
85
86 mutex_lock(&s2mpb02->i2c_lock);
87 ret = i2c_smbus_write_byte_data(i2c, reg, value);
88 mutex_unlock(&s2mpb02->i2c_lock);
89 if (ret < 0)
90 pr_info("%s:%s reg(0x%x), ret(%d)\n",
91 MFD_DEV_NAME, __func__, reg, ret);
92
93 return ret;
94 }
95 EXPORT_SYMBOL_GPL(s2mpb02_write_reg);
96
97 int s2mpb02_bulk_write(struct i2c_client *i2c, u8 reg, int count, u8 *buf)
98 {
99 struct s2mpb02_dev *s2mpb02 = i2c_get_clientdata(i2c);
100 int ret;
101
102 mutex_lock(&s2mpb02->i2c_lock);
103 ret = i2c_smbus_write_i2c_block_data(i2c, reg, count, buf);
104 mutex_unlock(&s2mpb02->i2c_lock);
105 if (ret < 0)
106 return ret;
107
108 return 0;
109 }
110 EXPORT_SYMBOL_GPL(s2mpb02_bulk_write);
111
112 int s2mpb02_update_reg(struct i2c_client *i2c, u8 reg, u8 val, u8 mask)
113 {
114 struct s2mpb02_dev *s2mpb02 = i2c_get_clientdata(i2c);
115 int ret;
116
117 mutex_lock(&s2mpb02->i2c_lock);
118 ret = i2c_smbus_read_byte_data(i2c, reg);
119 if (ret >= 0) {
120 u8 old_val = ret & 0xff;
121 u8 new_val = (val & mask) | (old_val & (~mask));
122 ret = i2c_smbus_write_byte_data(i2c, reg, new_val);
123 }
124 mutex_unlock(&s2mpb02->i2c_lock);
125 return ret;
126 }
127 EXPORT_SYMBOL_GPL(s2mpb02_update_reg);
128
129 #ifdef CONFIG_OF
130 static int of_s2mpb02_dt(struct device *dev,
131 struct s2mpb02_platform_data *pdata)
132 {
133 struct device_node *np_s2mpb02 = dev->of_node;
134
135 if (!np_s2mpb02)
136 return -EINVAL;
137
138 pdata->wakeup = of_property_read_bool(np_s2mpb02, "s2mpb02,wakeup");
139
140 return 0;
141 }
142 #else
143 static int of_s2mpb02_dt(struct device *dev,
144 struct s2mpb02_platform_data *pdata)
145 {
146 return 0;
147 }
148 #endif /* CONFIG_OF */
149
150 static int s2mpb02_i2c_probe(struct i2c_client *i2c,
151 const struct i2c_device_id *dev_id)
152 {
153 struct s2mpb02_dev *s2mpb02;
154 struct s2mpb02_platform_data *pdata = i2c->dev.platform_data;
155
156 int ret = 0;
157 u8 reg_data;
158
159 pr_info("%s:%s\n", MFD_DEV_NAME, __func__);
160
161 s2mpb02 = kzalloc(sizeof(struct s2mpb02_dev), GFP_KERNEL);
162 if (!s2mpb02) {
163 dev_err(&i2c->dev, "%s: Failed to alloc mem for s2mpb02\n",
164 __func__);
165 return -ENOMEM;
166 }
167
168 if (i2c->dev.of_node) {
169 pdata = devm_kzalloc(&i2c->dev,
170 sizeof(struct s2mpb02_platform_data), GFP_KERNEL);
171 if (!pdata) {
172 dev_err(&i2c->dev, "Failed to allocate memory\n");
173 ret = -ENOMEM;
174 goto err_pdata;
175 }
176
177 ret = of_s2mpb02_dt(&i2c->dev, pdata);
178 if (ret < 0) {
179 dev_err(&i2c->dev, "Failed to get device of_node\n");
180 goto err;
181 }
182
183 i2c->dev.platform_data = pdata;
184 } else
185 pdata = i2c->dev.platform_data;
186
187 s2mpb02->dev = &i2c->dev;
188 s2mpb02->i2c = i2c;
189 s2mpb02->irq = i2c->irq;
190 if (pdata) {
191 s2mpb02->pdata = pdata;
192
193 pdata->irq_base = irq_alloc_descs(-1, 600, S2MPB02_IRQ_NR, 0);
194 if (pdata->irq_base < 0) {
195 pr_err("%s:%s irq_alloc_descs Fail! ret(%d)\n",
196 MFD_DEV_NAME, __func__, pdata->irq_base);
197 ret = -EINVAL;
198 goto err;
199 } else {
200 s2mpb02->irq_base = pdata->irq_base;
201 }
202
203 s2mpb02->wakeup = pdata->wakeup;
204 } else {
205 ret = -EINVAL;
206 goto err;
207 }
208 mutex_init(&s2mpb02->i2c_lock);
209
210 i2c_set_clientdata(i2c, s2mpb02);
211
212 if (s2mpb02_read_reg(s2mpb02->i2c,
213 S2MPB02_REG_BST_CTRL2, &reg_data) < 0) {
214 pr_err("device not found on this channel!!\n");
215 ret = -ENODEV;
216 } else {
217 if (reg_data == 0x90) {
218 S2MPB02_PMIC_REV(s2mpb02) = 1;
219 } else
220 S2MPB02_PMIC_REV(s2mpb02) = 0;
221 pr_info("%s: device id 0x%x is found\n",
222 __func__, s2mpb02->rev_num);
223 }
224
225 ret = s2mpb02_irq_init(s2mpb02);
226 if (ret < 0)
227 goto err_irq_init;
228
229 ret = mfd_add_devices(s2mpb02->dev, -1, s2mpb02_devs,
230 ARRAY_SIZE(s2mpb02_devs), NULL, 0, NULL);
231 if (ret < 0)
232 goto err_irq_init;
233
234 device_init_wakeup(s2mpb02->dev, pdata->wakeup);
235
236 return ret;
237
238 err_irq_init:
239 mutex_destroy(&s2mpb02->i2c_lock);
240 err:
241 kfree(pdata);
242 s2mpb02_irq_exit(s2mpb02);
243 err_pdata:
244 kfree(s2mpb02);
245
246 return ret;
247 }
248
249 static int s2mpb02_i2c_remove(struct i2c_client *i2c)
250 {
251 struct s2mpb02_dev *s2mpb02 = i2c_get_clientdata(i2c);
252
253 mfd_remove_devices(s2mpb02->dev);
254 s2mpb02_irq_exit(s2mpb02);
255 mutex_destroy(&s2mpb02->i2c_lock);
256 kfree(s2mpb02);
257
258 return 0;
259 }
260
261 static const struct i2c_device_id s2mpb02_i2c_id[] = {
262 { MFD_DEV_NAME, TYPE_S2MPB02 },
263 { }
264 };
265 MODULE_DEVICE_TABLE(i2c, s2mpb02_i2c_id);
266
267 #if defined(CONFIG_OF)
268 static struct of_device_id s2mpb02_i2c_dt_ids[] = {
269 { .compatible = "s2mpb02,s2mpb02mfd" },
270 {},
271 };
272 MODULE_DEVICE_TABLE(of, s2mpb02_i2c_dt_ids);
273 #endif /* CONFIG_OF */
274
275 #if defined(CONFIG_PM)
276 static int s2mpb02_suspend(struct device *dev)
277 {
278 struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
279 struct s2mpb02_dev *s2mpb02 = i2c_get_clientdata(i2c);
280
281 if (device_may_wakeup(dev))
282 enable_irq_wake(s2mpb02->irq);
283
284 disable_irq(s2mpb02->irq);
285
286 return 0;
287 }
288
289 static int s2mpb02_resume(struct device *dev)
290 {
291 struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
292 struct s2mpb02_dev *s2mpb02 = i2c_get_clientdata(i2c);
293
294 if (device_may_wakeup(dev))
295 disable_irq_wake(s2mpb02->irq);
296
297 enable_irq(s2mpb02->irq);
298
299 return 0;
300 }
301 #else
302 #define s2mpb02_suspend NULL
303 #define s2mpb02_resume NULL
304 #endif /* CONFIG_PM */
305
306 const struct dev_pm_ops s2mpb02_pm = {
307 .suspend = s2mpb02_suspend,
308 .resume = s2mpb02_resume,
309 };
310
311 static struct i2c_driver s2mpb02_i2c_driver = {
312 .driver = {
313 .name = MFD_DEV_NAME,
314 .owner = THIS_MODULE,
315 #if defined(CONFIG_PM)
316 .pm = &s2mpb02_pm,
317 #endif /* CONFIG_PM */
318 #if defined(CONFIG_OF)
319 .of_match_table = s2mpb02_i2c_dt_ids,
320 #endif /* CONFIG_OF */
321 .suppress_bind_attrs = true,
322 },
323 .probe = s2mpb02_i2c_probe,
324 .remove = s2mpb02_i2c_remove,
325 .id_table = s2mpb02_i2c_id,
326 };
327
328 static int __init s2mpb02_i2c_init(void)
329 {
330 pr_info("%s:%s\n", MFD_DEV_NAME, __func__);
331 return i2c_add_driver(&s2mpb02_i2c_driver);
332 }
333 /* init early so consumer devices can complete system boot */
334 subsys_initcall(s2mpb02_i2c_init);
335
336 static void __exit s2mpb02_i2c_exit(void)
337 {
338 i2c_del_driver(&s2mpb02_i2c_driver);
339 }
340 module_exit(s2mpb02_i2c_exit);
341
342 MODULE_DESCRIPTION("SAMSUNG s2mpb02 multi-function core driver");
343 MODULE_LICENSE("GPL");