misc: tegra-baseband: support bypass/boost DC/DC
[linux-3.10.git] / drivers / misc / palmas-sim.c
1 /*
2  * Palmas SIM driver
3  *
4  * Copyright (c) 2013, NVIDIA CORPORATION.  All rights reserved.
5  * Author: Neil Patel <neilp@nvidia.com>
6  *
7  * This program is free software; you can redistribute it and/or modify it
8  * under the terms and conditions of the GNU General Public License,
9  * version 2, as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
14  * more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <linux/delay.h>
21 #include <linux/errno.h>
22 #include <linux/interrupt.h>
23 #include <linux/kernel.h>
24 #include <linux/miscdevice.h>
25 #include <linux/module.h>
26 #include <linux/mfd/palmas.h>
27 #include <linux/platform_device.h>
28
29 struct palmas_sim {
30         struct device   *parent;
31         struct device   *dev;
32         struct device   *miscdev;
33         struct palmas   *palmas;
34         int             sim1_irq;
35         int             sim2_irq;
36         bool            sim1_inserted;
37         bool            sim2_inserted;
38 };
39
40 static ssize_t palmas_sim1_state_read(struct device *dev,
41                 struct device_attribute *attr, char *buf)
42 {
43         struct palmas_sim *sim = dev_get_platdata(dev);
44
45         return sprintf(buf, "%s\n", sim->sim1_inserted ? "1" : "0");
46 }
47
48 static DEVICE_ATTR(sim1_inserted, S_IRUSR, palmas_sim1_state_read, NULL);
49
50 static ssize_t palmas_sim2_state_read(struct device *dev,
51                 struct device_attribute *attr, char *buf)
52 {
53         struct palmas_sim *sim = dev_get_platdata(dev);
54
55         return sprintf(buf, "%s\n", sim->sim2_inserted ? "1" : "0");
56 }
57 static DEVICE_ATTR(sim2_inserted, S_IRUSR, palmas_sim2_state_read, NULL);
58
59 static struct attribute *sim_attrs[] = {
60         &dev_attr_sim1_inserted.attr,
61         &dev_attr_sim2_inserted.attr,
62         NULL
63 };
64
65 static const struct attribute_group sim_attr_group = {
66         .attrs = sim_attrs,
67 };
68
69 static irqreturn_t palmas_sim_irq(int irq, void *data)
70 {
71         struct palmas_sim *sim = data;
72         bool sim1_inserted = false;
73         bool sim2_inserted = false;
74         int ret = 0;
75         int val;
76
77         dev_dbg(sim->dev, "sim irq %d hit\n", irq);
78
79         if (irq == sim->sim1_irq) {
80                 ret = palmas_read(sim->palmas, PALMAS_SIMCARD_BASE,
81                                         PALMAS_SIM_DEBOUNCE, &val);
82                 if (ret < 0)
83                         goto done;
84
85                 if (val & PALMAS_SIM_DEBOUNCE_SIM_DET1_PIN_STATE)
86                         sim1_inserted = true;
87
88                 if (sim1_inserted != sim->sim1_inserted) {
89                         ret = palmas_update_bits(sim->palmas,
90                                         PALMAS_SIMCARD_BASE,
91                                         PALMAS_SIM_DEBOUNCE,
92                                         PALMAS_SIM_DEBOUNCE_SIM1_IR,
93                                         (sim1_inserted ? 1 : 0) <<
94                                         PALMAS_SIM_DEBOUNCE_SIM1_IR_SHIFT);
95                         if (ret < 0)
96                                 goto done;
97
98                         sim->sim1_inserted = sim1_inserted;
99                         sysfs_notify(&sim->miscdev->kobj, NULL,
100                                 "sim1_inserted");
101                         dev_dbg(sim->dev, "sim1(%s)\n",
102                                 sim1_inserted ? "inserted" : "removed");
103                 }
104         } else if (irq == sim->sim2_irq) {
105                 ret = palmas_read(sim->palmas, PALMAS_SIMCARD_BASE,
106                                         PALMAS_SIM_PWR_DOWN, &val);
107                 if (ret < 0)
108                         goto done;
109
110                 if (val & PALMAS_SIM_PWR_DOWN_SIM_DET2_PIN_STATE)
111                         sim2_inserted = true;
112
113                 if (sim2_inserted != sim->sim2_inserted) {
114                         ret = palmas_update_bits(sim->palmas,
115                                         PALMAS_SIMCARD_BASE,
116                                         PALMAS_SIM_DEBOUNCE,
117                                         PALMAS_SIM_DEBOUNCE_SIM2_IR,
118                                         (sim2_inserted ? 1 : 0) <<
119                                         PALMAS_SIM_DEBOUNCE_SIM2_IR_SHIFT);
120                         if (ret < 0)
121                                 goto done;
122
123                         sim->sim2_inserted = sim2_inserted;
124                         sysfs_notify(&sim->miscdev->kobj, NULL,
125                                 "sim2_inserted");
126                         dev_dbg(sim->dev, "sim2(%s)\n",
127                                 sim2_inserted ? "inserted" : "removed");
128                 }
129         }
130
131 done:
132         if (ret)
133                 dev_err(sim->dev, "reg access failed, ret %d, irq %d\n", ret,
134                         irq);
135
136         return IRQ_HANDLED;
137 }
138
139 static struct miscdevice sim_miscdev = {
140         .minor = MISC_DYNAMIC_MINOR,
141         .name = "sim",
142 };
143
144 static int palmas_sim_probe(struct platform_device *pdev)
145 {
146         struct palmas_platform_data *pdata;
147         struct palmas_sim *sim;
148         struct palmas_sim_platform_data *sim_pdata;
149         struct palmas *palmas;
150         struct platform_device *misc_pdev;
151         int sim1_irq;
152         int sim2_irq;
153         bool sim1_inserted = false;
154         bool sim2_inserted = false;
155         int val;
156         int mask;
157         int ret;
158
159         pdata = dev_get_platdata(pdev->dev.parent);
160         if (!pdata) {
161                 dev_err(&pdev->dev, "Platform data not found\n");
162                 return -ENODEV;
163         }
164
165         sim_pdata = pdata->sim_pdata;
166         palmas = dev_get_drvdata(pdev->dev.parent);
167
168         sim1_irq = palmas_irq_get_virq(palmas, PALMAS_SIM1_IRQ);
169         if (sim1_irq <= 0) {
170                 dev_err(&pdev->dev, "sim1 interrupt is not available\n");
171                 return -ENODEV;
172         }
173
174         sim2_irq = palmas_irq_get_virq(palmas, PALMAS_SIM2_IRQ);
175         if (sim2_irq <= 0) {
176                 dev_err(&pdev->dev, "sim2 interrupt is not available\n");
177                 return -ENODEV;
178         }
179
180         /* set detection polarity - 0 is active high */
181         val = sim_pdata->det_polarity <<
182                 PALMAS_POLARITY_CTRL2_DET_POLARITY_SHIFT;
183         ret = palmas_update_bits(palmas, PALMAS_PU_PD_OD_BASE,
184                                 PALMAS_POLARITY_CTRL2,
185                                 PALMAS_POLARITY_CTRL2_DET_POLARITY, val);
186         if (ret < 0)
187                 goto err_reg_access;
188
189         /* initialize pull-ups and pull-downs */
190         val = (sim_pdata->det1_pu << PALMAS_PU_PD_INPUT_CTRL5_DET1_PU_SHIFT) |
191               (sim_pdata->det1_pd << PALMAS_PU_PD_INPUT_CTRL5_DET1_PD_SHIFT) |
192               (sim_pdata->det2_pu << PALMAS_PU_PD_INPUT_CTRL5_DET2_PU_SHIFT) |
193               (sim_pdata->det2_pd << PALMAS_PU_PD_INPUT_CTRL5_DET2_PD_SHIFT);
194         mask = (PALMAS_PU_PD_INPUT_CTRL5_DET1_PU |
195                 PALMAS_PU_PD_INPUT_CTRL5_DET1_PD |
196                 PALMAS_PU_PD_INPUT_CTRL5_DET2_PU |
197                 PALMAS_PU_PD_INPUT_CTRL5_DET2_PD);
198         ret = palmas_update_bits(palmas, PALMAS_PU_PD_OD_BASE,
199                                 PALMAS_PU_PD_INPUT_CTRL5, mask, val);
200         if (ret < 0)
201                 goto err_reg_access;
202
203         /* SIM_DEBOUNCE register configuration */
204         val = sim_pdata->dbcnt << PALMAS_SIM_DEBOUNCE_DBCNT_SHIFT;
205         ret = palmas_write(palmas, PALMAS_SIMCARD_BASE, PALMAS_SIM_DEBOUNCE,
206                                 val);
207         if (ret < 0)
208                 goto err_reg_access;
209
210         /* SIM_PWR_DOWN register configuration */
211         val = sim_pdata->pwrdnen2 << PALMAS_SIM_PWR_DOWN_PWRDNEN2_SHIFT;
212         val |= sim_pdata->pwrdnen1 << PALMAS_SIM_PWR_DOWN_PWRDNEN1_SHIFT;
213         val |= sim_pdata->pwrdncnt << PALMAS_SIM_PWR_DOWN_PWRDNCNT_SHIFT;
214         ret = palmas_write(palmas, PALMAS_SIMCARD_BASE, PALMAS_SIM_PWR_DOWN,
215                                 val);
216         if (ret < 0)
217                 goto err_reg_access;
218
219         /* 1 ms is the max debounce time */
220         usleep_range(1000, 2000);
221
222         /* read sim1 state */
223         ret = palmas_read(palmas, PALMAS_SIMCARD_BASE, PALMAS_SIM_DEBOUNCE,
224                                 &val);
225         if (ret < 0)
226                 goto err_reg_access;
227         if (val & PALMAS_SIM_DEBOUNCE_SIM_DET1_PIN_STATE)
228                 sim1_inserted = true;
229
230         /* read sim2 state */
231         ret = palmas_read(palmas, PALMAS_SIMCARD_BASE, PALMAS_SIM_PWR_DOWN,
232                                 &val);
233         if (ret < 0)
234                 goto err_reg_access;
235         if (val & PALMAS_SIM_PWR_DOWN_SIM_DET2_PIN_STATE)
236                 sim2_inserted = true;
237
238         if (misc_register(&sim_miscdev) != 0) {
239                 dev_err(&pdev->dev, "sim: cannot register miscdev\n");
240                 return -ENODEV;
241         }
242
243         if (sysfs_create_group(&sim_miscdev.this_device->kobj,
244                                 &sim_attr_group)) {
245                 dev_err(&pdev->dev, "sysfs group create fails\n");
246                 ret = -ENODEV;
247                 goto err_sysfs;
248         }
249
250         sim = devm_kzalloc(&pdev->dev, sizeof(*sim), GFP_KERNEL);
251         if (!sim) {
252                 dev_err(&pdev->dev, "Could not allocate palmas_sim\n");
253                 ret = -ENOMEM;
254                 goto err_malloc;
255         }
256
257         sim->parent = pdev->dev.parent;
258         sim->dev = &pdev->dev;
259         sim->miscdev = sim_miscdev.this_device;
260         sim->sim1_irq = sim1_irq;
261         sim->sim2_irq = sim2_irq;
262         sim->sim1_inserted = sim1_inserted;
263         sim->sim2_inserted = sim2_inserted;
264         sim->palmas = palmas;
265         platform_set_drvdata(pdev, sim);
266
267         misc_pdev = to_platform_device(sim_miscdev.this_device);
268         misc_pdev->dev.platform_data = sim;
269
270         ret = request_threaded_irq(sim1_irq, NULL, palmas_sim_irq,
271                                 IRQF_ONESHOT, "SIM1_DETECT_IRQ", sim);
272         if (ret < 0) {
273                 dev_err(&pdev->dev, "sim1 irq request fails\n");
274                 ret = -ENODEV;
275                 goto err_malloc;
276         }
277
278         ret = request_threaded_irq(sim2_irq, NULL, palmas_sim_irq,
279                                 IRQF_ONESHOT, "SIM2_DETECT_IRQ", sim);
280         if (ret < 0) {
281                 dev_err(&pdev->dev, "sim2 irq request fails\n");
282                 ret = -ENODEV;
283                 goto err_sim1_irq;
284         }
285
286         /* enable insertion/removal detection based on sim status */
287         val = ((sim1_inserted ? 1 : 0) << PALMAS_SIM_DEBOUNCE_SIM1_IR_SHIFT) |
288               ((sim2_inserted ? 1 : 0) << PALMAS_SIM_DEBOUNCE_SIM2_IR_SHIFT);
289         ret = palmas_update_bits(palmas, PALMAS_SIMCARD_BASE,
290                                 PALMAS_SIM_DEBOUNCE,
291                                 PALMAS_SIM_DEBOUNCE_SIM1_IR |
292                                 PALMAS_SIM_DEBOUNCE_SIM2_IR, val);
293         if (ret < 0)
294                 goto err_sim2_irq;
295
296         return 0;
297
298 err_sim2_irq:
299         free_irq(sim2_irq, sim);
300 err_sim1_irq:
301         free_irq(sim1_irq, sim);
302 err_malloc:
303         sysfs_remove_group(&sim_miscdev.this_device->kobj, &sim_attr_group);
304 err_sysfs:
305         misc_deregister(&sim_miscdev);
306 err_reg_access:
307         dev_err(&pdev->dev, "probe fails due to reg access %d\n", ret);
308
309         return ret;
310 }
311
312 static int palmas_sim_remove(struct platform_device *pdev)
313 {
314         struct palmas_sim *sim = platform_get_drvdata(pdev);
315
316         free_irq(sim->sim2_irq, sim);
317         free_irq(sim->sim1_irq, sim);
318         sysfs_remove_group(&sim_miscdev.this_device->kobj, &sim_attr_group);
319         misc_deregister(&sim_miscdev);
320
321         return 0;
322 }
323
324 static struct platform_driver palmas_sim_driver = {
325         .driver.name    = "palmas-sim",
326         .driver.owner   = THIS_MODULE,
327         .probe          = palmas_sim_probe,
328         .remove         = palmas_sim_remove,
329 };
330
331 static int __init palmas_sim_init(void)
332 {
333         return platform_driver_register(&palmas_sim_driver);
334 }
335 subsys_initcall(palmas_sim_init);
336
337 static void __exit palmas_sim_exit(void)
338 {
339         platform_driver_unregister(&palmas_sim_driver);
340 }
341 module_exit(palmas_sim_exit);
342
343 MODULE_DESCRIPTION("sim driver for palmas pmic");
344 MODULE_VERSION("1.0");
345 MODULE_LICENSE("GPL v2");
346 MODULE_AUTHOR("Neil Patel <neilp@nvidia.com>");
347 MODULE_ALIAS("platform:palmas-sim");