xref: /openbmc/linux/drivers/mfd/max77843.c (revision dc0c386e)
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, &reg_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