CHROMIUM: mfd: s5m: Enable RTC as wakeup source in suspend.
[cascardo/linux.git] / drivers / mfd / s5m-core.c
1 /*
2  * s5m87xx.c
3  *
4  * Copyright (c) 2011 Samsung Electronics Co., Ltd
5  *              http://www.samsung.com
6  *
7  *  This program is free software; you can redistribute  it and/or modify it
8  *  under  the terms of  the GNU General  Public License as published by the
9  *  Free Software Foundation;  either version 2 of the  License, or (at your
10  *  option) any later version.
11  *
12  */
13
14 #include <linux/module.h>
15 #include <linux/moduleparam.h>
16 #include <linux/init.h>
17 #include <linux/err.h>
18 #include <linux/slab.h>
19 #include <linux/i2c.h>
20 #include <linux/interrupt.h>
21 #include <linux/pm_runtime.h>
22 #include <linux/mutex.h>
23 #include <linux/mfd/core.h>
24 #include <linux/mfd/s5m87xx/s5m-core.h>
25 #include <linux/mfd/s5m87xx/s5m-pmic.h>
26 #include <linux/mfd/s5m87xx/s5m-rtc.h>
27 #include <linux/regmap.h>
28
29 #ifdef CONFIG_OF
30 static struct of_device_id __devinitdata s5m87xx_pmic_dt_match[] = {
31         {.compatible = "samsung,s5m8767-pmic"},
32         {},
33 };
34 #endif
35
36 static struct mfd_cell s5m8751_devs[] = {
37         {
38                 .name = "s5m8751-pmic",
39         }, {
40                 .name = "s5m-charger",
41         }, {
42                 .name = "s5m8751-codec",
43         },
44 };
45
46 static struct mfd_cell s5m8763_devs[] = {
47         {
48                 .name = "s5m8763-pmic",
49         }, {
50                 .name = "s5m-rtc",
51         }, {
52                 .name = "s5m-charger",
53         },
54 };
55
56 static struct mfd_cell s5m8767_devs[] = {
57         {
58                 .name = "s5m8767-pmic",
59         }, {
60                 .name = "s5m-rtc",
61         },
62 };
63
64 static struct regmap_config s5m_regmap_config = {
65         .reg_bits = 8,
66         .val_bits = 8,
67 };
68
69 static inline int s5m87xx_i2c_get_driver_data(struct i2c_client *i2c,
70                                                const struct i2c_device_id *id)
71 {
72 #ifdef CONFIG_OF
73         if (i2c->dev.of_node) {
74                 const struct of_device_id *match;
75                 match = of_match_node(s5m87xx_pmic_dt_match, i2c->dev.of_node);
76                 return (int)match->data;
77         }
78 #endif
79         return (int)id->driver_data;
80 }
81
82 #ifdef CONFIG_OF
83 static struct s5m_platform_data *s5m87xx_i2c_parse_dt_pdata(struct device *dev)
84 {
85         struct s5m_platform_data *pd;
86
87         pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL);
88         if (!pd) {
89                 dev_err(dev, "could not allocate memory for pdata\n");
90                 return ERR_PTR(-ENOMEM);
91         }
92
93         if (of_property_read_u32(dev->of_node, "s5m-core,device_type",
94                                  &pd->device_type)) {
95                 dev_warn(dev, "no OF device_type property");
96         }
97         dev_dbg(dev, "OF device_type property = %u", pd->device_type);
98
99         if (of_get_property(dev->of_node, "s5m-core,enable-low-jitter", NULL)) {
100                 if (!(pd->device_type == S5M8767X))
101                         dev_warn(dev, "no low-jitter for this PMIC type\n");
102                 else
103                         pd->low_jitter = true;
104         }
105         dev_dbg(dev, "OF low-jitter property: %u\n", pd->low_jitter);
106
107         return pd;
108 }
109 #else
110 static struct s5m_platform_data *s5m87xx_i2c_parse_dt_pdata(struct device *dev)
111 {
112         return 0;
113 }
114 #endif
115
116 static int s5m87xx_set_low_jitter(struct s5m87xx_dev *s5m87xx)
117 {
118         if (!s5m87xx->pdata->low_jitter)
119                 return 0;
120
121         return regmap_update_bits(s5m87xx->pmic, S5M8767_REG_CTRL1,
122                                   S5M8767_LOW_JITTER_MASK,
123                                   S5M8767_LOW_JITTER_MASK);
124 }
125
126 static int __devinit s5m87xx_i2c_probe(struct i2c_client *i2c,
127                                        const struct i2c_device_id *id)
128 {
129         struct s5m_platform_data *pdata = i2c->dev.platform_data;
130         struct s5m87xx_dev *s5m87xx;
131         struct i2c_client *rtc_i2c = NULL;
132         int ret;
133
134         s5m87xx = devm_kzalloc(&i2c->dev, sizeof(struct s5m87xx_dev),
135                                 GFP_KERNEL);
136         if (s5m87xx == NULL)
137                 return -ENOMEM;
138
139         i2c_set_clientdata(i2c, s5m87xx);
140         s5m87xx->dev = &i2c->dev;
141         s5m87xx->irq = i2c->irq;
142         s5m87xx->type = s5m87xx_i2c_get_driver_data(i2c, id);
143
144         if (s5m87xx->dev->of_node) {
145                 pdata = s5m87xx_i2c_parse_dt_pdata(s5m87xx->dev);
146                 if (IS_ERR(pdata)) {
147                         ret = PTR_ERR(pdata);
148                         goto err;
149                 }
150         }
151         s5m87xx->pdata = pdata;
152
153         if (!pdata) {
154                 ret = -ENODEV;
155                 dev_warn(s5m87xx->dev, "No platform data found\n");
156                 goto err;
157         }
158
159         s5m87xx->device_type = pdata->device_type;
160         /* TODO(tbroch): address whether we want this addtional interrupt node
161            and add it to DT parsing if yes.
162         */
163         s5m87xx->ono = pdata->ono;
164         /* TODO(tbroch): remove hack below and parse irq_base via DT */
165         s5m87xx->irq_base = pdata->irq_base = MAX77686_IRQ_BASE;
166 #ifdef OF_CONFIG
167         s5m87xx->wakeup = i2c->flags & I2C_CLIENT_WAKE;
168 #else
169         s5m87xx->wakeup = pdata->wakeup;
170 #endif
171
172         s5m87xx->pmic = regmap_init_i2c(i2c, &s5m_regmap_config);
173         if (IS_ERR(s5m87xx->pmic)) {
174                 ret = PTR_ERR(s5m87xx->pmic);
175                 dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
176                         ret);
177                 goto err;
178         }
179
180         rtc_i2c = i2c_new_dummy(i2c->adapter, RTC_I2C_ADDR);
181         i2c_set_clientdata(rtc_i2c, s5m87xx);
182         s5m87xx->rtc = regmap_init_i2c(rtc_i2c, &s5m_regmap_config);
183         if (IS_ERR(s5m87xx->rtc)) {
184                 ret = PTR_ERR(s5m87xx->rtc);
185                 dev_err(&rtc_i2c->dev, "Failed to allocate register map: %d\n",
186                         ret);
187                 goto err;
188         }
189
190         if (pdata && pdata->cfg_pmic_irq)
191                 pdata->cfg_pmic_irq();
192
193         s5m_irq_init(s5m87xx);
194
195         pm_runtime_set_active(s5m87xx->dev);
196
197         switch (s5m87xx->device_type) {
198         case S5M8751X:
199                 ret = mfd_add_devices(s5m87xx->dev, -1, s5m8751_devs,
200                                       ARRAY_SIZE(s5m8751_devs), NULL, 0);
201                 break;
202         case S5M8763X:
203                 ret = mfd_add_devices(s5m87xx->dev, -1, s5m8763_devs,
204                                       ARRAY_SIZE(s5m8763_devs), NULL, 0);
205                 break;
206         case S5M8767X:
207                 ret = mfd_add_devices(s5m87xx->dev, -1, s5m8767_devs,
208                                       ARRAY_SIZE(s5m8767_devs), NULL, 0);
209                 break;
210         default:
211                 /* If this happens the probe function is problem */
212                 BUG();
213         }
214
215         if (ret < 0)
216                 goto err;
217
218         if (s5m87xx_set_low_jitter(s5m87xx) < 0) {
219                 dev_err(s5m87xx->dev, "failed to configure low-jitter\n");
220                 ret = -EIO;
221                 goto err;
222         }
223
224         return ret;
225
226 err:
227         mfd_remove_devices(s5m87xx->dev);
228         s5m_irq_exit(s5m87xx);
229         if (rtc_i2c)
230                 i2c_unregister_device(rtc_i2c);
231         regmap_exit(s5m87xx->pmic);
232         regmap_exit(s5m87xx->rtc);
233         return ret;
234 }
235
236 static int s5m87xx_i2c_remove(struct i2c_client *i2c)
237 {
238         struct s5m87xx_dev *s5m87xx = i2c_get_clientdata(i2c);
239
240         device_init_wakeup(s5m87xx->dev, 0);
241         pm_runtime_set_suspended(s5m87xx->dev);
242         mfd_remove_devices(s5m87xx->dev);
243         s5m_irq_exit(s5m87xx);
244         regmap_exit(s5m87xx->pmic);
245         regmap_exit(s5m87xx->rtc);
246         return 0;
247 }
248
249 static const struct i2c_device_id s5m87xx_i2c_id[] = {
250         { "s5m87xx", 0 },
251         { }
252 };
253 MODULE_DEVICE_TABLE(i2c, s5m87xx_i2c_id);
254
255 #ifdef CONFIG_PM
256 static int s5m87xx_suspend(struct device *dev)
257 {
258         struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
259         struct s5m87xx_dev *s5m87xx = i2c_get_clientdata(i2c);
260
261         if (device_may_wakeup(dev))
262                 enable_irq_wake(s5m87xx->irq);
263
264         return 0;
265 }
266
267 static int s5m87xx_resume(struct device *dev)
268 {
269         struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
270         struct s5m87xx_dev *s5m87xx = i2c_get_clientdata(i2c);
271
272         if (device_may_wakeup(dev))
273                 disable_irq_wake(s5m87xx->irq);
274
275         s5m_irq_resume(s5m87xx);
276         return 0;
277 }
278
279 const struct dev_pm_ops s5m87xx_pm = {
280         .suspend = s5m87xx_suspend,
281         .resume = s5m87xx_resume,
282 };
283 #endif
284
285 static struct i2c_driver s5m87xx_i2c_driver = {
286         .driver = {
287                    .name = "s5m87xx",
288                    .owner = THIS_MODULE,
289 #ifdef CONFIG_PM
290                    .pm = &s5m87xx_pm,
291 #endif
292                    .of_match_table = of_match_ptr(s5m87xx_pmic_dt_match),
293         },
294         .probe = s5m87xx_i2c_probe,
295         .remove = s5m87xx_i2c_remove,
296         .id_table = s5m87xx_i2c_id,
297 };
298
299 static int __init s5m87xx_i2c_init(void)
300 {
301         return i2c_add_driver(&s5m87xx_i2c_driver);
302 }
303
304 subsys_initcall(s5m87xx_i2c_init);
305
306 static void __exit s5m87xx_i2c_exit(void)
307 {
308         i2c_del_driver(&s5m87xx_i2c_driver);
309 }
310 module_exit(s5m87xx_i2c_exit);
311
312 MODULE_AUTHOR("Sangbeom Kim <sbkim73@samsung.com>");
313 MODULE_DESCRIPTION("Core support for the S5M MFD");
314 MODULE_LICENSE("GPL");