xref: /openbmc/linux/drivers/mfd/max77843.c (revision 13c6de60)
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>
16c7f585feSJaewon Kim #include <linux/of_device.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 	.mask_invert	= false,
63c7f585feSJaewon Kim 	.num_regs	= 1,
64c7f585feSJaewon Kim 	.irqs		= max77843_irqs,
65c7f585feSJaewon Kim 	.num_irqs	= ARRAY_SIZE(max77843_irqs),
66c7f585feSJaewon Kim };
67c7f585feSJaewon Kim 
68c7f585feSJaewon Kim /* Charger and Charger regulator use same regmap. */
69bc1aadc1SKrzysztof Kozlowski static int max77843_chg_init(struct max77693_dev *max77843)
70c7f585feSJaewon Kim {
71c7f585feSJaewon Kim 	int ret;
72c7f585feSJaewon Kim 
730005a9e1SWolfram Sang 	max77843->i2c_chg = i2c_new_dummy_device(max77843->i2c->adapter, I2C_ADDR_CHG);
740005a9e1SWolfram Sang 	if (IS_ERR(max77843->i2c_chg)) {
75c7f585feSJaewon Kim 		dev_err(&max77843->i2c->dev,
76c7f585feSJaewon Kim 				"Cannot allocate I2C device for Charger\n");
770005a9e1SWolfram Sang 		return PTR_ERR(max77843->i2c_chg);
78c7f585feSJaewon Kim 	}
79c7f585feSJaewon Kim 	i2c_set_clientdata(max77843->i2c_chg, max77843);
80c7f585feSJaewon Kim 
81c7f585feSJaewon Kim 	max77843->regmap_chg = devm_regmap_init_i2c(max77843->i2c_chg,
82c7f585feSJaewon Kim 			&max77843_charger_regmap_config);
83c7f585feSJaewon Kim 	if (IS_ERR(max77843->regmap_chg)) {
84c7f585feSJaewon Kim 		ret = PTR_ERR(max77843->regmap_chg);
85c7f585feSJaewon Kim 		goto err_chg_i2c;
86c7f585feSJaewon Kim 	}
87c7f585feSJaewon Kim 
88c7f585feSJaewon Kim 	return 0;
89c7f585feSJaewon Kim 
90c7f585feSJaewon Kim err_chg_i2c:
91c7f585feSJaewon Kim 	i2c_unregister_device(max77843->i2c_chg);
92c7f585feSJaewon Kim 
93c7f585feSJaewon Kim 	return ret;
94c7f585feSJaewon Kim }
95c7f585feSJaewon Kim 
96*13c6de60SUwe Kleine-König static int max77843_probe(struct i2c_client *i2c)
97c7f585feSJaewon Kim {
98*13c6de60SUwe Kleine-König 	const struct i2c_device_id *id = i2c_client_get_device_id(i2c);
99bc1aadc1SKrzysztof Kozlowski 	struct max77693_dev *max77843;
100c7f585feSJaewon Kim 	unsigned int reg_data;
101c7f585feSJaewon Kim 	int ret;
102c7f585feSJaewon Kim 
103c7f585feSJaewon Kim 	max77843 = devm_kzalloc(&i2c->dev, sizeof(*max77843), GFP_KERNEL);
104c7f585feSJaewon Kim 	if (!max77843)
105c7f585feSJaewon Kim 		return -ENOMEM;
106c7f585feSJaewon Kim 
107c7f585feSJaewon Kim 	i2c_set_clientdata(i2c, max77843);
108c7f585feSJaewon Kim 	max77843->dev = &i2c->dev;
109c7f585feSJaewon Kim 	max77843->i2c = i2c;
110c7f585feSJaewon Kim 	max77843->irq = i2c->irq;
111bc1aadc1SKrzysztof Kozlowski 	max77843->type = id->driver_data;
112c7f585feSJaewon Kim 
113c7f585feSJaewon Kim 	max77843->regmap = devm_regmap_init_i2c(i2c,
114c7f585feSJaewon Kim 			&max77843_regmap_config);
115c7f585feSJaewon Kim 	if (IS_ERR(max77843->regmap)) {
116c7f585feSJaewon Kim 		dev_err(&i2c->dev, "Failed to allocate topsys register map\n");
117c7f585feSJaewon Kim 		return PTR_ERR(max77843->regmap);
118c7f585feSJaewon Kim 	}
119c7f585feSJaewon Kim 
120c7f585feSJaewon Kim 	ret = regmap_add_irq_chip(max77843->regmap, max77843->irq,
121c7f585feSJaewon Kim 			IRQF_TRIGGER_LOW | IRQF_ONESHOT | IRQF_SHARED,
122bc1aadc1SKrzysztof Kozlowski 			0, &max77843_irq_chip, &max77843->irq_data_topsys);
123c7f585feSJaewon Kim 	if (ret) {
124c7f585feSJaewon Kim 		dev_err(&i2c->dev, "Failed to add TOPSYS IRQ chip\n");
125c7f585feSJaewon Kim 		return ret;
126c7f585feSJaewon Kim 	}
127c7f585feSJaewon Kim 
128c7f585feSJaewon Kim 	ret = regmap_read(max77843->regmap,
129c7f585feSJaewon Kim 			MAX77843_SYS_REG_PMICID, &reg_data);
130c7f585feSJaewon Kim 	if (ret < 0) {
131c7f585feSJaewon Kim 		dev_err(&i2c->dev, "Failed to read PMIC ID\n");
132c7f585feSJaewon Kim 		goto err_pmic_id;
133c7f585feSJaewon Kim 	}
134c7f585feSJaewon Kim 	dev_info(&i2c->dev, "device ID: 0x%x\n", reg_data);
135c7f585feSJaewon Kim 
136c7f585feSJaewon Kim 	ret = max77843_chg_init(max77843);
137c7f585feSJaewon Kim 	if (ret) {
138c7f585feSJaewon Kim 		dev_err(&i2c->dev, "Failed to init Charger\n");
139c7f585feSJaewon Kim 		goto err_pmic_id;
140c7f585feSJaewon Kim 	}
141c7f585feSJaewon Kim 
142c7f585feSJaewon Kim 	ret = regmap_update_bits(max77843->regmap,
143c7f585feSJaewon Kim 				 MAX77843_SYS_REG_INTSRCMASK,
144c7f585feSJaewon Kim 				 MAX77843_INTSRC_MASK_MASK,
145c7f585feSJaewon Kim 				 (unsigned int)~MAX77843_INTSRC_MASK_MASK);
146c7f585feSJaewon Kim 	if (ret < 0) {
147c7f585feSJaewon Kim 		dev_err(&i2c->dev, "Failed to unmask interrupt source\n");
148c7f585feSJaewon Kim 		goto err_pmic_id;
149c7f585feSJaewon Kim 	}
150c7f585feSJaewon Kim 
151c7f585feSJaewon Kim 	ret = mfd_add_devices(max77843->dev, -1, max77843_devs,
152c7f585feSJaewon Kim 			      ARRAY_SIZE(max77843_devs), NULL, 0, NULL);
153c7f585feSJaewon Kim 	if (ret < 0) {
154c7f585feSJaewon Kim 		dev_err(&i2c->dev, "Failed to add mfd device\n");
155c7f585feSJaewon Kim 		goto err_pmic_id;
156c7f585feSJaewon Kim 	}
157c7f585feSJaewon Kim 
158c7f585feSJaewon Kim 	device_init_wakeup(max77843->dev, true);
159c7f585feSJaewon Kim 
160c7f585feSJaewon Kim 	return 0;
161c7f585feSJaewon Kim 
162c7f585feSJaewon Kim err_pmic_id:
163bc1aadc1SKrzysztof Kozlowski 	regmap_del_irq_chip(max77843->irq, max77843->irq_data_topsys);
164c7f585feSJaewon Kim 
165c7f585feSJaewon Kim 	return ret;
166c7f585feSJaewon Kim }
167c7f585feSJaewon Kim 
168c7f585feSJaewon Kim static const struct of_device_id max77843_dt_match[] = {
169c7f585feSJaewon Kim 	{ .compatible = "maxim,max77843", },
170c7f585feSJaewon Kim 	{ },
171c7f585feSJaewon Kim };
172c7f585feSJaewon Kim 
173c7f585feSJaewon Kim static const struct i2c_device_id max77843_id[] = {
174bc1aadc1SKrzysztof Kozlowski 	{ "max77843", TYPE_MAX77843, },
175c7f585feSJaewon Kim 	{ },
176c7f585feSJaewon Kim };
177c7f585feSJaewon Kim 
178c7f585feSJaewon Kim static int __maybe_unused max77843_suspend(struct device *dev)
179c7f585feSJaewon Kim {
1801b5420e1SGeliang Tang 	struct i2c_client *i2c = to_i2c_client(dev);
181bc1aadc1SKrzysztof Kozlowski 	struct max77693_dev *max77843 = i2c_get_clientdata(i2c);
182c7f585feSJaewon Kim 
183c7f585feSJaewon Kim 	disable_irq(max77843->irq);
184c7f585feSJaewon Kim 	if (device_may_wakeup(dev))
185c7f585feSJaewon Kim 		enable_irq_wake(max77843->irq);
186c7f585feSJaewon Kim 
187c7f585feSJaewon Kim 	return 0;
188c7f585feSJaewon Kim }
189c7f585feSJaewon Kim 
190c7f585feSJaewon Kim static int __maybe_unused max77843_resume(struct device *dev)
191c7f585feSJaewon Kim {
1921b5420e1SGeliang Tang 	struct i2c_client *i2c = to_i2c_client(dev);
193bc1aadc1SKrzysztof Kozlowski 	struct max77693_dev *max77843 = i2c_get_clientdata(i2c);
194c7f585feSJaewon Kim 
195c7f585feSJaewon Kim 	if (device_may_wakeup(dev))
196c7f585feSJaewon Kim 		disable_irq_wake(max77843->irq);
197c7f585feSJaewon Kim 	enable_irq(max77843->irq);
198c7f585feSJaewon Kim 
199c7f585feSJaewon Kim 	return 0;
200c7f585feSJaewon Kim }
201c7f585feSJaewon Kim 
202c7f585feSJaewon Kim static SIMPLE_DEV_PM_OPS(max77843_pm, max77843_suspend, max77843_resume);
203c7f585feSJaewon Kim 
204c7f585feSJaewon Kim static struct i2c_driver max77843_i2c_driver = {
205c7f585feSJaewon Kim 	.driver	= {
206c7f585feSJaewon Kim 		.name = "max77843",
207c7f585feSJaewon Kim 		.pm = &max77843_pm,
208c7f585feSJaewon Kim 		.of_match_table = max77843_dt_match,
20938f70da3SPaul Gortmaker 		.suppress_bind_attrs = true,
210c7f585feSJaewon Kim 	},
211*13c6de60SUwe Kleine-König 	.probe_new = max77843_probe,
212c7f585feSJaewon Kim 	.id_table = max77843_id,
213c7f585feSJaewon Kim };
214c7f585feSJaewon Kim 
215c7f585feSJaewon Kim static int __init max77843_i2c_init(void)
216c7f585feSJaewon Kim {
217c7f585feSJaewon Kim 	return i2c_add_driver(&max77843_i2c_driver);
218c7f585feSJaewon Kim }
219c7f585feSJaewon Kim subsys_initcall(max77843_i2c_init);
220