1d7d8d7a2SKrzysztof Kozlowski // SPDX-License-Identifier: GPL-2.0+
2d7d8d7a2SKrzysztof Kozlowski //
3d7d8d7a2SKrzysztof Kozlowski // MFD core driver for the Maxim MAX77843
4d7d8d7a2SKrzysztof Kozlowski //
5d7d8d7a2SKrzysztof Kozlowski // Copyright (C) 2015 Samsung Electronics
6d7d8d7a2SKrzysztof Kozlowski // Author: Jaewon Kim <jaewon02.kim@samsung.com>
7d7d8d7a2SKrzysztof Kozlowski // Author: Beomho Seo <beomho.seo@samsung.com>
8c7f585feSJaewon Kim
9c7f585feSJaewon Kim #include <linux/err.h>
10c7f585feSJaewon Kim #include <linux/i2c.h>
11c7f585feSJaewon Kim #include <linux/init.h>
12c7f585feSJaewon Kim #include <linux/interrupt.h>
13c7f585feSJaewon Kim #include <linux/mfd/core.h>
14bc1aadc1SKrzysztof Kozlowski #include <linux/mfd/max77693-common.h>
15c7f585feSJaewon Kim #include <linux/mfd/max77843-private.h>
16*dc0c386eSRob Herring #include <linux/mod_devicetable.h>
17c7f585feSJaewon Kim #include <linux/platform_device.h>
18c7f585feSJaewon Kim
19c7f585feSJaewon Kim static const struct mfd_cell max77843_devs[] = {
20c7f585feSJaewon Kim {
21c7f585feSJaewon Kim .name = "max77843-muic",
22c7f585feSJaewon Kim .of_compatible = "maxim,max77843-muic",
23c7f585feSJaewon Kim }, {
24c7f585feSJaewon Kim .name = "max77843-regulator",
25c7f585feSJaewon Kim .of_compatible = "maxim,max77843-regulator",
26c7f585feSJaewon Kim }, {
27c7f585feSJaewon Kim .name = "max77843-charger",
28c7f585feSJaewon Kim .of_compatible = "maxim,max77843-charger"
29c7f585feSJaewon Kim }, {
30c7f585feSJaewon Kim .name = "max77843-fuelgauge",
31c7f585feSJaewon Kim .of_compatible = "maxim,max77843-fuelgauge",
32c7f585feSJaewon Kim }, {
33c7f585feSJaewon Kim .name = "max77843-haptic",
34c7f585feSJaewon Kim .of_compatible = "maxim,max77843-haptic",
35c7f585feSJaewon Kim },
36c7f585feSJaewon Kim };
37c7f585feSJaewon Kim
38c7f585feSJaewon Kim static const struct regmap_config max77843_charger_regmap_config = {
39c7f585feSJaewon Kim .reg_bits = 8,
40c7f585feSJaewon Kim .val_bits = 8,
41c7f585feSJaewon Kim .max_register = MAX77843_CHG_REG_END,
42c7f585feSJaewon Kim };
43c7f585feSJaewon Kim
44c7f585feSJaewon Kim static const struct regmap_config max77843_regmap_config = {
45c7f585feSJaewon Kim .reg_bits = 8,
46c7f585feSJaewon Kim .val_bits = 8,
47c7f585feSJaewon Kim .max_register = MAX77843_SYS_REG_END,
48c7f585feSJaewon Kim };
49c7f585feSJaewon Kim
50c7f585feSJaewon Kim static const struct regmap_irq max77843_irqs[] = {
51c7f585feSJaewon Kim /* TOPSYS interrupts */
52c7f585feSJaewon Kim { .reg_offset = 0, .mask = MAX77843_SYS_IRQ_SYSUVLO_INT, },
53c7f585feSJaewon Kim { .reg_offset = 0, .mask = MAX77843_SYS_IRQ_SYSOVLO_INT, },
54c7f585feSJaewon Kim { .reg_offset = 0, .mask = MAX77843_SYS_IRQ_TSHDN_INT, },
55c7f585feSJaewon Kim { .reg_offset = 0, .mask = MAX77843_SYS_IRQ_TM_INT, },
56c7f585feSJaewon Kim };
57c7f585feSJaewon Kim
58c7f585feSJaewon Kim static const struct regmap_irq_chip max77843_irq_chip = {
59c7f585feSJaewon Kim .name = "max77843",
60c7f585feSJaewon Kim .status_base = MAX77843_SYS_REG_SYSINTSRC,
61c7f585feSJaewon Kim .mask_base = MAX77843_SYS_REG_SYSINTMASK,
62c7f585feSJaewon Kim .num_regs = 1,
63c7f585feSJaewon Kim .irqs = max77843_irqs,
64c7f585feSJaewon Kim .num_irqs = ARRAY_SIZE(max77843_irqs),
65c7f585feSJaewon Kim };
66c7f585feSJaewon Kim
67c7f585feSJaewon Kim /* Charger and Charger regulator use same regmap. */
max77843_chg_init(struct max77693_dev * max77843)68bc1aadc1SKrzysztof Kozlowski static int max77843_chg_init(struct max77693_dev *max77843)
69c7f585feSJaewon Kim {
70c7f585feSJaewon Kim int ret;
71c7f585feSJaewon Kim
720005a9e1SWolfram Sang max77843->i2c_chg = i2c_new_dummy_device(max77843->i2c->adapter, I2C_ADDR_CHG);
730005a9e1SWolfram Sang if (IS_ERR(max77843->i2c_chg)) {
74c7f585feSJaewon Kim dev_err(&max77843->i2c->dev,
75c7f585feSJaewon Kim "Cannot allocate I2C device for Charger\n");
760005a9e1SWolfram Sang return PTR_ERR(max77843->i2c_chg);
77c7f585feSJaewon Kim }
78c7f585feSJaewon Kim i2c_set_clientdata(max77843->i2c_chg, max77843);
79c7f585feSJaewon Kim
80c7f585feSJaewon Kim max77843->regmap_chg = devm_regmap_init_i2c(max77843->i2c_chg,
81c7f585feSJaewon Kim &max77843_charger_regmap_config);
82c7f585feSJaewon Kim if (IS_ERR(max77843->regmap_chg)) {
83c7f585feSJaewon Kim ret = PTR_ERR(max77843->regmap_chg);
84c7f585feSJaewon Kim goto err_chg_i2c;
85c7f585feSJaewon Kim }
86c7f585feSJaewon Kim
87c7f585feSJaewon Kim return 0;
88c7f585feSJaewon Kim
89c7f585feSJaewon Kim err_chg_i2c:
90c7f585feSJaewon Kim i2c_unregister_device(max77843->i2c_chg);
91c7f585feSJaewon Kim
92c7f585feSJaewon Kim return ret;
93c7f585feSJaewon Kim }
94c7f585feSJaewon Kim
max77843_probe(struct i2c_client * i2c)9513c6de60SUwe Kleine-König static int max77843_probe(struct i2c_client *i2c)
96c7f585feSJaewon Kim {
9713c6de60SUwe Kleine-König const struct i2c_device_id *id = i2c_client_get_device_id(i2c);
98bc1aadc1SKrzysztof Kozlowski struct max77693_dev *max77843;
99c7f585feSJaewon Kim unsigned int reg_data;
100c7f585feSJaewon Kim int ret;
101c7f585feSJaewon Kim
102c7f585feSJaewon Kim max77843 = devm_kzalloc(&i2c->dev, sizeof(*max77843), GFP_KERNEL);
103c7f585feSJaewon Kim if (!max77843)
104c7f585feSJaewon Kim return -ENOMEM;
105c7f585feSJaewon Kim
106c7f585feSJaewon Kim i2c_set_clientdata(i2c, max77843);
107c7f585feSJaewon Kim max77843->dev = &i2c->dev;
108c7f585feSJaewon Kim max77843->i2c = i2c;
109c7f585feSJaewon Kim max77843->irq = i2c->irq;
110bc1aadc1SKrzysztof Kozlowski max77843->type = id->driver_data;
111c7f585feSJaewon Kim
112c7f585feSJaewon Kim max77843->regmap = devm_regmap_init_i2c(i2c,
113c7f585feSJaewon Kim &max77843_regmap_config);
114c7f585feSJaewon Kim if (IS_ERR(max77843->regmap)) {
115c7f585feSJaewon Kim dev_err(&i2c->dev, "Failed to allocate topsys register map\n");
116c7f585feSJaewon Kim return PTR_ERR(max77843->regmap);
117c7f585feSJaewon Kim }
118c7f585feSJaewon Kim
119c7f585feSJaewon Kim ret = regmap_add_irq_chip(max77843->regmap, max77843->irq,
120c7f585feSJaewon Kim IRQF_TRIGGER_LOW | IRQF_ONESHOT | IRQF_SHARED,
121bc1aadc1SKrzysztof Kozlowski 0, &max77843_irq_chip, &max77843->irq_data_topsys);
122c7f585feSJaewon Kim if (ret) {
123c7f585feSJaewon Kim dev_err(&i2c->dev, "Failed to add TOPSYS IRQ chip\n");
124c7f585feSJaewon Kim return ret;
125c7f585feSJaewon Kim }
126c7f585feSJaewon Kim
127c7f585feSJaewon Kim ret = regmap_read(max77843->regmap,
128c7f585feSJaewon Kim MAX77843_SYS_REG_PMICID, ®_data);
129c7f585feSJaewon Kim if (ret < 0) {
130c7f585feSJaewon Kim dev_err(&i2c->dev, "Failed to read PMIC ID\n");
131c7f585feSJaewon Kim goto err_pmic_id;
132c7f585feSJaewon Kim }
133c7f585feSJaewon Kim dev_info(&i2c->dev, "device ID: 0x%x\n", reg_data);
134c7f585feSJaewon Kim
135c7f585feSJaewon Kim ret = max77843_chg_init(max77843);
136c7f585feSJaewon Kim if (ret) {
137c7f585feSJaewon Kim dev_err(&i2c->dev, "Failed to init Charger\n");
138c7f585feSJaewon Kim goto err_pmic_id;
139c7f585feSJaewon Kim }
140c7f585feSJaewon Kim
141c7f585feSJaewon Kim ret = regmap_update_bits(max77843->regmap,
142c7f585feSJaewon Kim MAX77843_SYS_REG_INTSRCMASK,
143c7f585feSJaewon Kim MAX77843_INTSRC_MASK_MASK,
144c7f585feSJaewon Kim (unsigned int)~MAX77843_INTSRC_MASK_MASK);
145c7f585feSJaewon Kim if (ret < 0) {
146c7f585feSJaewon Kim dev_err(&i2c->dev, "Failed to unmask interrupt source\n");
147c7f585feSJaewon Kim goto err_pmic_id;
148c7f585feSJaewon Kim }
149c7f585feSJaewon Kim
150c7f585feSJaewon Kim ret = mfd_add_devices(max77843->dev, -1, max77843_devs,
151c7f585feSJaewon Kim ARRAY_SIZE(max77843_devs), NULL, 0, NULL);
152c7f585feSJaewon Kim if (ret < 0) {
153c7f585feSJaewon Kim dev_err(&i2c->dev, "Failed to add mfd device\n");
154c7f585feSJaewon Kim goto err_pmic_id;
155c7f585feSJaewon Kim }
156c7f585feSJaewon Kim
157c7f585feSJaewon Kim device_init_wakeup(max77843->dev, true);
158c7f585feSJaewon Kim
159c7f585feSJaewon Kim return 0;
160c7f585feSJaewon Kim
161c7f585feSJaewon Kim err_pmic_id:
162bc1aadc1SKrzysztof Kozlowski regmap_del_irq_chip(max77843->irq, max77843->irq_data_topsys);
163c7f585feSJaewon Kim
164c7f585feSJaewon Kim return ret;
165c7f585feSJaewon Kim }
166c7f585feSJaewon Kim
167c7f585feSJaewon Kim static const struct of_device_id max77843_dt_match[] = {
168c7f585feSJaewon Kim { .compatible = "maxim,max77843", },
169c7f585feSJaewon Kim { },
170c7f585feSJaewon Kim };
171c7f585feSJaewon Kim
172c7f585feSJaewon Kim static const struct i2c_device_id max77843_id[] = {
173bc1aadc1SKrzysztof Kozlowski { "max77843", TYPE_MAX77843, },
174c7f585feSJaewon Kim { },
175c7f585feSJaewon Kim };
176c7f585feSJaewon Kim
max77843_suspend(struct device * dev)177c7f585feSJaewon Kim static int __maybe_unused max77843_suspend(struct device *dev)
178c7f585feSJaewon Kim {
1791b5420e1SGeliang Tang struct i2c_client *i2c = to_i2c_client(dev);
180bc1aadc1SKrzysztof Kozlowski struct max77693_dev *max77843 = i2c_get_clientdata(i2c);
181c7f585feSJaewon Kim
182c7f585feSJaewon Kim disable_irq(max77843->irq);
183c7f585feSJaewon Kim if (device_may_wakeup(dev))
184c7f585feSJaewon Kim enable_irq_wake(max77843->irq);
185c7f585feSJaewon Kim
186c7f585feSJaewon Kim return 0;
187c7f585feSJaewon Kim }
188c7f585feSJaewon Kim
max77843_resume(struct device * dev)189c7f585feSJaewon Kim static int __maybe_unused max77843_resume(struct device *dev)
190c7f585feSJaewon Kim {
1911b5420e1SGeliang Tang struct i2c_client *i2c = to_i2c_client(dev);
192bc1aadc1SKrzysztof Kozlowski struct max77693_dev *max77843 = i2c_get_clientdata(i2c);
193c7f585feSJaewon Kim
194c7f585feSJaewon Kim if (device_may_wakeup(dev))
195c7f585feSJaewon Kim disable_irq_wake(max77843->irq);
196c7f585feSJaewon Kim enable_irq(max77843->irq);
197c7f585feSJaewon Kim
198c7f585feSJaewon Kim return 0;
199c7f585feSJaewon Kim }
200c7f585feSJaewon Kim
201c7f585feSJaewon Kim static SIMPLE_DEV_PM_OPS(max77843_pm, max77843_suspend, max77843_resume);
202c7f585feSJaewon Kim
203c7f585feSJaewon Kim static struct i2c_driver max77843_i2c_driver = {
204c7f585feSJaewon Kim .driver = {
205c7f585feSJaewon Kim .name = "max77843",
206c7f585feSJaewon Kim .pm = &max77843_pm,
207c7f585feSJaewon Kim .of_match_table = max77843_dt_match,
20838f70da3SPaul Gortmaker .suppress_bind_attrs = true,
209c7f585feSJaewon Kim },
2109816d859SUwe Kleine-König .probe = max77843_probe,
211c7f585feSJaewon Kim .id_table = max77843_id,
212c7f585feSJaewon Kim };
213c7f585feSJaewon Kim
max77843_i2c_init(void)214c7f585feSJaewon Kim static int __init max77843_i2c_init(void)
215c7f585feSJaewon Kim {
216c7f585feSJaewon Kim return i2c_add_driver(&max77843_i2c_driver);
217c7f585feSJaewon Kim }
218c7f585feSJaewon Kim subsys_initcall(max77843_i2c_init);
219