xref: /linux/drivers/mfd/max77705.c (revision 69352bd52b2667e5c6e8ebb14143528c28f5e37d)
1c8d50f02SDzmitry Sankouski // SPDX-License-Identifier: GPL-2.0+
2c8d50f02SDzmitry Sankouski /*
3c8d50f02SDzmitry Sankouski  * Maxim MAX77705 PMIC core driver
4c8d50f02SDzmitry Sankouski  *
5c8d50f02SDzmitry Sankouski  * Copyright (C) 2025 Dzmitry Sankouski <dsankouski@gmail.com>
6c8d50f02SDzmitry Sankouski  **/
7c8d50f02SDzmitry Sankouski #include <linux/i2c.h>
8c8d50f02SDzmitry Sankouski #include <linux/interrupt.h>
9c8d50f02SDzmitry Sankouski #include <linux/mfd/core.h>
10c8d50f02SDzmitry Sankouski #include <linux/mfd/max77705-private.h>
11c8d50f02SDzmitry Sankouski #include <linux/mfd/max77693-common.h>
12c8d50f02SDzmitry Sankouski #include <linux/pm.h>
13c8d50f02SDzmitry Sankouski #include <linux/power/max17042_battery.h>
14c8d50f02SDzmitry Sankouski #include <linux/module.h>
15c8d50f02SDzmitry Sankouski #include <linux/regmap.h>
16c8d50f02SDzmitry Sankouski #include <linux/of.h>
17c8d50f02SDzmitry Sankouski 
18c8d50f02SDzmitry Sankouski static struct mfd_cell max77705_devs[] = {
19c8d50f02SDzmitry Sankouski 	MFD_CELL_OF("max77705-rgb", NULL, NULL, 0, 0, "maxim,max77705-rgb"),
20c8d50f02SDzmitry Sankouski 	MFD_CELL_OF("max77705-charger", NULL, NULL, 0, 0, "maxim,max77705-charger"),
21c8d50f02SDzmitry Sankouski 	MFD_CELL_OF("max77705-haptic", NULL, NULL, 0, 0, "maxim,max77705-haptic"),
22c8d50f02SDzmitry Sankouski };
23c8d50f02SDzmitry Sankouski 
24c8d50f02SDzmitry Sankouski static const struct regmap_range max77705_readable_ranges[] = {
25c8d50f02SDzmitry Sankouski 	regmap_reg_range(MAX77705_PMIC_REG_PMICID1,		MAX77705_PMIC_REG_BSTOUT_MASK),
26c8d50f02SDzmitry Sankouski 	regmap_reg_range(MAX77705_PMIC_REG_INTSRC,		MAX77705_PMIC_REG_RESERVED_29),
27c8d50f02SDzmitry Sankouski 	regmap_reg_range(MAX77705_PMIC_REG_BOOSTCONTROL1,	MAX77705_PMIC_REG_BOOSTCONTROL1),
28c8d50f02SDzmitry Sankouski 	regmap_reg_range(MAX77705_PMIC_REG_MCONFIG,		MAX77705_PMIC_REG_MCONFIG2),
29c8d50f02SDzmitry Sankouski 	regmap_reg_range(MAX77705_PMIC_REG_FORCE_EN_MASK,	MAX77705_PMIC_REG_FORCE_EN_MASK),
30c8d50f02SDzmitry Sankouski 	regmap_reg_range(MAX77705_PMIC_REG_BOOSTCONTROL1,	MAX77705_PMIC_REG_BOOSTCONTROL1),
31c8d50f02SDzmitry Sankouski 	regmap_reg_range(MAX77705_PMIC_REG_BOOSTCONTROL2,	MAX77705_PMIC_REG_BOOSTCONTROL2),
32c8d50f02SDzmitry Sankouski 	regmap_reg_range(MAX77705_PMIC_REG_SW_RESET,		MAX77705_PMIC_REG_USBC_RESET),
33c8d50f02SDzmitry Sankouski };
34c8d50f02SDzmitry Sankouski 
35c8d50f02SDzmitry Sankouski static const struct regmap_range max77705_writable_ranges[] = {
36c8d50f02SDzmitry Sankouski 	regmap_reg_range(MAX77705_PMIC_REG_MAINCTRL1,		MAX77705_PMIC_REG_BSTOUT_MASK),
37c8d50f02SDzmitry Sankouski 	regmap_reg_range(MAX77705_PMIC_REG_INTSRC,		MAX77705_PMIC_REG_RESERVED_29),
38c8d50f02SDzmitry Sankouski 	regmap_reg_range(MAX77705_PMIC_REG_BOOSTCONTROL1,	MAX77705_PMIC_REG_BOOSTCONTROL1),
39c8d50f02SDzmitry Sankouski 	regmap_reg_range(MAX77705_PMIC_REG_MCONFIG,		MAX77705_PMIC_REG_MCONFIG2),
40c8d50f02SDzmitry Sankouski 	regmap_reg_range(MAX77705_PMIC_REG_FORCE_EN_MASK,	MAX77705_PMIC_REG_FORCE_EN_MASK),
41c8d50f02SDzmitry Sankouski 	regmap_reg_range(MAX77705_PMIC_REG_BOOSTCONTROL1,	MAX77705_PMIC_REG_BOOSTCONTROL1),
42c8d50f02SDzmitry Sankouski 	regmap_reg_range(MAX77705_PMIC_REG_BOOSTCONTROL2,	MAX77705_PMIC_REG_BOOSTCONTROL2),
43c8d50f02SDzmitry Sankouski 	regmap_reg_range(MAX77705_PMIC_REG_SW_RESET,		MAX77705_PMIC_REG_USBC_RESET),
44c8d50f02SDzmitry Sankouski };
45c8d50f02SDzmitry Sankouski 
46c8d50f02SDzmitry Sankouski static const struct regmap_access_table max77705_readable_table = {
47c8d50f02SDzmitry Sankouski 	.yes_ranges = max77705_readable_ranges,
48c8d50f02SDzmitry Sankouski 	.n_yes_ranges = ARRAY_SIZE(max77705_readable_ranges),
49c8d50f02SDzmitry Sankouski };
50c8d50f02SDzmitry Sankouski 
51c8d50f02SDzmitry Sankouski static const struct regmap_access_table max77705_writable_table = {
52c8d50f02SDzmitry Sankouski 	.yes_ranges = max77705_writable_ranges,
53c8d50f02SDzmitry Sankouski 	.n_yes_ranges = ARRAY_SIZE(max77705_writable_ranges),
54c8d50f02SDzmitry Sankouski };
55c8d50f02SDzmitry Sankouski 
56c8d50f02SDzmitry Sankouski static const struct regmap_config max77705_regmap_config = {
57c8d50f02SDzmitry Sankouski 	.reg_bits = 8,
58c8d50f02SDzmitry Sankouski 	.val_bits = 8,
59c8d50f02SDzmitry Sankouski 	.rd_table = &max77705_readable_table,
60c8d50f02SDzmitry Sankouski 	.wr_table = &max77705_writable_table,
61c8d50f02SDzmitry Sankouski 	.max_register = MAX77705_PMIC_REG_USBC_RESET,
62c8d50f02SDzmitry Sankouski };
63c8d50f02SDzmitry Sankouski 
64c8d50f02SDzmitry Sankouski static const struct regmap_irq max77705_topsys_irqs[] = {
65c8d50f02SDzmitry Sankouski 	{ .mask = MAX77705_SYSTEM_IRQ_BSTEN_INT, },
66c8d50f02SDzmitry Sankouski 	{ .mask = MAX77705_SYSTEM_IRQ_SYSUVLO_INT, },
67c8d50f02SDzmitry Sankouski 	{ .mask = MAX77705_SYSTEM_IRQ_SYSOVLO_INT, },
68c8d50f02SDzmitry Sankouski 	{ .mask = MAX77705_SYSTEM_IRQ_TSHDN_INT, },
69c8d50f02SDzmitry Sankouski 	{ .mask = MAX77705_SYSTEM_IRQ_TM_INT, },
70c8d50f02SDzmitry Sankouski };
71c8d50f02SDzmitry Sankouski 
72c8d50f02SDzmitry Sankouski static const struct regmap_irq_chip max77705_topsys_irq_chip = {
73c8d50f02SDzmitry Sankouski 	.name		= "max77705-topsys",
74c8d50f02SDzmitry Sankouski 	.status_base	= MAX77705_PMIC_REG_SYSTEM_INT,
75c8d50f02SDzmitry Sankouski 	.mask_base	= MAX77705_PMIC_REG_SYSTEM_INT_MASK,
76c8d50f02SDzmitry Sankouski 	.num_regs	= 1,
77c8d50f02SDzmitry Sankouski 	.irqs		= max77705_topsys_irqs,
78c8d50f02SDzmitry Sankouski 	.num_irqs	= ARRAY_SIZE(max77705_topsys_irqs),
79c8d50f02SDzmitry Sankouski };
80c8d50f02SDzmitry Sankouski 
81c8d50f02SDzmitry Sankouski static int max77705_i2c_probe(struct i2c_client *i2c)
82c8d50f02SDzmitry Sankouski {
83c8d50f02SDzmitry Sankouski 	struct device *dev = &i2c->dev;
84c8d50f02SDzmitry Sankouski 	struct max77693_dev *max77705;
85c8d50f02SDzmitry Sankouski 	struct regmap_irq_chip_data *irq_data;
86c8d50f02SDzmitry Sankouski 	struct irq_domain *domain;
87c8d50f02SDzmitry Sankouski 	enum max77705_hw_rev pmic_rev;
88c8d50f02SDzmitry Sankouski 	unsigned int pmic_rev_value;
89c8d50f02SDzmitry Sankouski 	int ret;
90c8d50f02SDzmitry Sankouski 
91c8d50f02SDzmitry Sankouski 	max77705 = devm_kzalloc(dev, sizeof(*max77705), GFP_KERNEL);
92c8d50f02SDzmitry Sankouski 	if (!max77705)
93c8d50f02SDzmitry Sankouski 		return -ENOMEM;
94c8d50f02SDzmitry Sankouski 
95c8d50f02SDzmitry Sankouski 	max77705->i2c = i2c;
96c8d50f02SDzmitry Sankouski 	max77705->type = TYPE_MAX77705;
97c8d50f02SDzmitry Sankouski 	i2c_set_clientdata(i2c, max77705);
98c8d50f02SDzmitry Sankouski 
99c8d50f02SDzmitry Sankouski 	max77705->regmap = devm_regmap_init_i2c(i2c, &max77705_regmap_config);
100c8d50f02SDzmitry Sankouski 	if (IS_ERR(max77705->regmap))
101c8d50f02SDzmitry Sankouski 		return PTR_ERR(max77705->regmap);
102c8d50f02SDzmitry Sankouski 
103c8d50f02SDzmitry Sankouski 	ret = regmap_read(max77705->regmap, MAX77705_PMIC_REG_PMICREV, &pmic_rev_value);
104c8d50f02SDzmitry Sankouski 	if (ret < 0)
105c8d50f02SDzmitry Sankouski 		return -ENODEV;
106c8d50f02SDzmitry Sankouski 
107c8d50f02SDzmitry Sankouski 	pmic_rev = pmic_rev_value & MAX77705_REVISION_MASK;
108c8d50f02SDzmitry Sankouski 	if (pmic_rev != MAX77705_PASS3)
109c8d50f02SDzmitry Sankouski 		return dev_err_probe(dev, -ENODEV, "Rev.0x%x is not tested\n", pmic_rev);
110c8d50f02SDzmitry Sankouski 
111c8d50f02SDzmitry Sankouski 	ret = devm_regmap_add_irq_chip(dev, max77705->regmap,
112c8d50f02SDzmitry Sankouski 					i2c->irq,
113c8d50f02SDzmitry Sankouski 					IRQF_ONESHOT | IRQF_SHARED, 0,
114c8d50f02SDzmitry Sankouski 					&max77705_topsys_irq_chip,
115c8d50f02SDzmitry Sankouski 					&irq_data);
116c8d50f02SDzmitry Sankouski 	if (ret)
117c8d50f02SDzmitry Sankouski 		return dev_err_probe(dev, ret, "Failed to add IRQ chip\n");
118c8d50f02SDzmitry Sankouski 
119c8d50f02SDzmitry Sankouski 	/* Unmask interrupts from all blocks in interrupt source register */
120c8d50f02SDzmitry Sankouski 	ret = regmap_update_bits(max77705->regmap,
121c8d50f02SDzmitry Sankouski 				 MAX77705_PMIC_REG_INTSRC_MASK,
122c8d50f02SDzmitry Sankouski 				 MAX77705_SRC_IRQ_ALL, (unsigned int)~MAX77705_SRC_IRQ_ALL);
123c8d50f02SDzmitry Sankouski 	if (ret < 0)
124c8d50f02SDzmitry Sankouski 		return dev_err_probe(dev, ret, "Could not unmask interrupts in INTSRC\n");
125c8d50f02SDzmitry Sankouski 
126c8d50f02SDzmitry Sankouski 	domain = regmap_irq_get_domain(irq_data);
127c8d50f02SDzmitry Sankouski 
128c8d50f02SDzmitry Sankouski 	ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE,
129c8d50f02SDzmitry Sankouski 				   max77705_devs, ARRAY_SIZE(max77705_devs),
130c8d50f02SDzmitry Sankouski 				   NULL, 0, domain);
131c8d50f02SDzmitry Sankouski 	if (ret)
132c8d50f02SDzmitry Sankouski 		return dev_err_probe(dev, ret, "Failed to register child devices\n");
133c8d50f02SDzmitry Sankouski 
134*a59a56ccSKrzysztof Kozlowski 	ret = devm_device_init_wakeup(dev);
135*a59a56ccSKrzysztof Kozlowski 	if (ret)
136*a59a56ccSKrzysztof Kozlowski 		return dev_err_probe(dev, ret, "Failed to init wakeup\n");
137c8d50f02SDzmitry Sankouski 
138c8d50f02SDzmitry Sankouski 	return 0;
139c8d50f02SDzmitry Sankouski }
140c8d50f02SDzmitry Sankouski 
141c8d50f02SDzmitry Sankouski static int max77705_suspend(struct device *dev)
142c8d50f02SDzmitry Sankouski {
143c8d50f02SDzmitry Sankouski 	struct i2c_client *i2c = to_i2c_client(dev);
144c8d50f02SDzmitry Sankouski 
145c8d50f02SDzmitry Sankouski 	disable_irq(i2c->irq);
146c8d50f02SDzmitry Sankouski 
147c8d50f02SDzmitry Sankouski 	if (device_may_wakeup(dev))
148c8d50f02SDzmitry Sankouski 		enable_irq_wake(i2c->irq);
149c8d50f02SDzmitry Sankouski 
150c8d50f02SDzmitry Sankouski 	return 0;
151c8d50f02SDzmitry Sankouski }
152c8d50f02SDzmitry Sankouski 
153c8d50f02SDzmitry Sankouski static int max77705_resume(struct device *dev)
154c8d50f02SDzmitry Sankouski {
155c8d50f02SDzmitry Sankouski 	struct i2c_client *i2c = to_i2c_client(dev);
156c8d50f02SDzmitry Sankouski 
157c8d50f02SDzmitry Sankouski 	if (device_may_wakeup(dev))
158c8d50f02SDzmitry Sankouski 		disable_irq_wake(i2c->irq);
159c8d50f02SDzmitry Sankouski 
160c8d50f02SDzmitry Sankouski 	enable_irq(i2c->irq);
161c8d50f02SDzmitry Sankouski 
162c8d50f02SDzmitry Sankouski 	return 0;
163c8d50f02SDzmitry Sankouski }
164c8d50f02SDzmitry Sankouski DEFINE_SIMPLE_DEV_PM_OPS(max77705_pm_ops, max77705_suspend, max77705_resume);
165c8d50f02SDzmitry Sankouski 
166c8d50f02SDzmitry Sankouski static const struct of_device_id max77705_i2c_of_match[] = {
167c8d50f02SDzmitry Sankouski 	{ .compatible = "maxim,max77705" },
168c8d50f02SDzmitry Sankouski 	{ }
169c8d50f02SDzmitry Sankouski };
170c8d50f02SDzmitry Sankouski MODULE_DEVICE_TABLE(of, max77705_i2c_of_match);
171c8d50f02SDzmitry Sankouski 
172c8d50f02SDzmitry Sankouski static struct i2c_driver max77705_i2c_driver = {
173c8d50f02SDzmitry Sankouski 	.driver = {
174c8d50f02SDzmitry Sankouski 		.name			= "max77705",
175c8d50f02SDzmitry Sankouski 		.of_match_table		= max77705_i2c_of_match,
176c8d50f02SDzmitry Sankouski 		.pm			= pm_sleep_ptr(&max77705_pm_ops),
177c8d50f02SDzmitry Sankouski 	},
178c8d50f02SDzmitry Sankouski 	.probe = max77705_i2c_probe
179c8d50f02SDzmitry Sankouski };
180c8d50f02SDzmitry Sankouski module_i2c_driver(max77705_i2c_driver);
181c8d50f02SDzmitry Sankouski 
182c8d50f02SDzmitry Sankouski MODULE_DESCRIPTION("Maxim MAX77705 PMIC core driver");
183c8d50f02SDzmitry Sankouski MODULE_AUTHOR("Dzmitry Sankouski <dsankouski@gmail.com>");
184c8d50f02SDzmitry Sankouski MODULE_LICENSE("GPL");
185