4 * Copyright (c) 2011 Samsung Electronics Co., Ltd
5 * http://www.samsung.com
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.
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>
30 static struct of_device_id __devinitdata s5m87xx_pmic_dt_match[] = {
31 {.compatible = "samsung,s5m8767-pmic"},
36 static struct mfd_cell s5m8751_devs[] = {
38 .name = "s5m8751-pmic",
40 .name = "s5m-charger",
42 .name = "s5m8751-codec",
46 static struct mfd_cell s5m8763_devs[] = {
48 .name = "s5m8763-pmic",
52 .name = "s5m-charger",
56 static struct mfd_cell s5m8767_devs[] = {
58 .name = "s5m8767-pmic",
64 static struct regmap_config s5m_regmap_config = {
69 static inline int s5m87xx_i2c_get_driver_data(struct i2c_client *i2c,
70 const struct i2c_device_id *id)
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;
79 return (int)id->driver_data;
83 static struct s5m_platform_data *s5m87xx_i2c_parse_dt_pdata(struct device *dev)
85 struct s5m_platform_data *pd;
87 pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL);
89 dev_err(dev, "could not allocate memory for pdata\n");
90 return ERR_PTR(-ENOMEM);
93 if (of_property_read_u32(dev->of_node, "s5m-core,device_type",
95 dev_warn(dev, "no OF device_type property");
97 dev_dbg(dev, "OF device_type property = %u", pd->device_type);
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");
103 pd->low_jitter = true;
105 dev_dbg(dev, "OF low-jitter property: %u\n", pd->low_jitter);
110 static struct s5m_platform_data *s5m87xx_i2c_parse_dt_pdata(struct device *dev)
116 static int s5m87xx_set_low_jitter(struct s5m87xx_dev *s5m87xx)
118 if (!s5m87xx->pdata->low_jitter)
121 return regmap_update_bits(s5m87xx->pmic, S5M8767_REG_CTRL1,
122 S5M8767_LOW_JITTER_MASK,
123 S5M8767_LOW_JITTER_MASK);
126 static int __devinit s5m87xx_i2c_probe(struct i2c_client *i2c,
127 const struct i2c_device_id *id)
129 struct s5m_platform_data *pdata = i2c->dev.platform_data;
130 struct s5m87xx_dev *s5m87xx;
131 struct i2c_client *rtc_i2c = NULL;
134 s5m87xx = devm_kzalloc(&i2c->dev, sizeof(struct s5m87xx_dev),
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);
144 if (s5m87xx->dev->of_node) {
145 pdata = s5m87xx_i2c_parse_dt_pdata(s5m87xx->dev);
147 ret = PTR_ERR(pdata);
151 s5m87xx->pdata = pdata;
155 dev_warn(s5m87xx->dev, "No platform data found\n");
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.
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;
167 s5m87xx->wakeup = i2c->flags & I2C_CLIENT_WAKE;
169 s5m87xx->wakeup = pdata->wakeup;
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",
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",
190 if (pdata && pdata->cfg_pmic_irq)
191 pdata->cfg_pmic_irq();
193 s5m_irq_init(s5m87xx);
195 pm_runtime_set_active(s5m87xx->dev);
197 switch (s5m87xx->device_type) {
199 ret = mfd_add_devices(s5m87xx->dev, -1, s5m8751_devs,
200 ARRAY_SIZE(s5m8751_devs), NULL, 0);
203 ret = mfd_add_devices(s5m87xx->dev, -1, s5m8763_devs,
204 ARRAY_SIZE(s5m8763_devs), NULL, 0);
207 ret = mfd_add_devices(s5m87xx->dev, -1, s5m8767_devs,
208 ARRAY_SIZE(s5m8767_devs), NULL, 0);
211 /* If this happens the probe function is problem */
218 if (s5m87xx_set_low_jitter(s5m87xx) < 0) {
219 dev_err(s5m87xx->dev, "failed to configure low-jitter\n");
227 mfd_remove_devices(s5m87xx->dev);
228 s5m_irq_exit(s5m87xx);
230 i2c_unregister_device(rtc_i2c);
231 regmap_exit(s5m87xx->pmic);
232 regmap_exit(s5m87xx->rtc);
236 static int s5m87xx_i2c_remove(struct i2c_client *i2c)
238 struct s5m87xx_dev *s5m87xx = i2c_get_clientdata(i2c);
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);
249 static const struct i2c_device_id s5m87xx_i2c_id[] = {
253 MODULE_DEVICE_TABLE(i2c, s5m87xx_i2c_id);
256 static int s5m87xx_suspend(struct device *dev)
258 struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
259 struct s5m87xx_dev *s5m87xx = i2c_get_clientdata(i2c);
261 if (device_may_wakeup(dev))
262 enable_irq_wake(s5m87xx->irq);
267 static int s5m87xx_resume(struct device *dev)
269 struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
270 struct s5m87xx_dev *s5m87xx = i2c_get_clientdata(i2c);
272 if (device_may_wakeup(dev))
273 disable_irq_wake(s5m87xx->irq);
275 s5m_irq_resume(s5m87xx);
279 const struct dev_pm_ops s5m87xx_pm = {
280 .suspend = s5m87xx_suspend,
281 .resume = s5m87xx_resume,
285 static struct i2c_driver s5m87xx_i2c_driver = {
288 .owner = THIS_MODULE,
292 .of_match_table = of_match_ptr(s5m87xx_pmic_dt_match),
294 .probe = s5m87xx_i2c_probe,
295 .remove = s5m87xx_i2c_remove,
296 .id_table = s5m87xx_i2c_id,
299 static int __init s5m87xx_i2c_init(void)
301 return i2c_add_driver(&s5m87xx_i2c_driver);
304 subsys_initcall(s5m87xx_i2c_init);
306 static void __exit s5m87xx_i2c_exit(void)
308 i2c_del_driver(&s5m87xx_i2c_driver);
310 module_exit(s5m87xx_i2c_exit);
312 MODULE_AUTHOR("Sangbeom Kim <sbkim73@samsung.com>");
313 MODULE_DESCRIPTION("Core support for the S5M MFD");
314 MODULE_LICENSE("GPL");