xref: /openbmc/linux/drivers/mfd/axp20x.c (revision 6774def6)
1 /*
2  * axp20x.c - MFD core driver for the X-Powers AXP202 and AXP209
3  *
4  * AXP20x comprises an adaptive USB-Compatible PWM charger, 2 BUCK DC-DC
5  * converters, 5 LDOs, multiple 12-bit ADCs of voltage, current and temperature
6  * as well as 4 configurable GPIOs.
7  *
8  * Author: Carlo Caione <carlo@caione.org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License version 2 as
12  * published by the Free Software Foundation.
13  */
14 
15 #include <linux/err.h>
16 #include <linux/i2c.h>
17 #include <linux/interrupt.h>
18 #include <linux/kernel.h>
19 #include <linux/module.h>
20 #include <linux/pm_runtime.h>
21 #include <linux/regmap.h>
22 #include <linux/slab.h>
23 #include <linux/regulator/consumer.h>
24 #include <linux/mfd/axp20x.h>
25 #include <linux/mfd/core.h>
26 #include <linux/of_device.h>
27 #include <linux/of_irq.h>
28 
29 #define AXP20X_OFF	0x80
30 
31 static const struct regmap_range axp20x_writeable_ranges[] = {
32 	regmap_reg_range(AXP20X_DATACACHE(0), AXP20X_IRQ5_STATE),
33 	regmap_reg_range(AXP20X_DCDC_MODE, AXP20X_FG_RES),
34 };
35 
36 static const struct regmap_range axp20x_volatile_ranges[] = {
37 	regmap_reg_range(AXP20X_IRQ1_EN, AXP20X_IRQ5_STATE),
38 };
39 
40 static const struct regmap_access_table axp20x_writeable_table = {
41 	.yes_ranges	= axp20x_writeable_ranges,
42 	.n_yes_ranges	= ARRAY_SIZE(axp20x_writeable_ranges),
43 };
44 
45 static const struct regmap_access_table axp20x_volatile_table = {
46 	.yes_ranges	= axp20x_volatile_ranges,
47 	.n_yes_ranges	= ARRAY_SIZE(axp20x_volatile_ranges),
48 };
49 
50 static struct resource axp20x_pek_resources[] = {
51 	{
52 		.name	= "PEK_DBR",
53 		.start	= AXP20X_IRQ_PEK_RIS_EDGE,
54 		.end	= AXP20X_IRQ_PEK_RIS_EDGE,
55 		.flags	= IORESOURCE_IRQ,
56 	}, {
57 		.name	= "PEK_DBF",
58 		.start	= AXP20X_IRQ_PEK_FAL_EDGE,
59 		.end	= AXP20X_IRQ_PEK_FAL_EDGE,
60 		.flags	= IORESOURCE_IRQ,
61 	},
62 };
63 
64 static const struct regmap_config axp20x_regmap_config = {
65 	.reg_bits	= 8,
66 	.val_bits	= 8,
67 	.wr_table	= &axp20x_writeable_table,
68 	.volatile_table	= &axp20x_volatile_table,
69 	.max_register	= AXP20X_FG_RES,
70 	.cache_type	= REGCACHE_RBTREE,
71 };
72 
73 #define AXP20X_IRQ(_irq, _off, _mask) \
74 	[AXP20X_IRQ_##_irq] = { .reg_offset = (_off), .mask = BIT(_mask) }
75 
76 static const struct regmap_irq axp20x_regmap_irqs[] = {
77 	AXP20X_IRQ(ACIN_OVER_V,		0, 7),
78 	AXP20X_IRQ(ACIN_PLUGIN,		0, 6),
79 	AXP20X_IRQ(ACIN_REMOVAL,	0, 5),
80 	AXP20X_IRQ(VBUS_OVER_V,		0, 4),
81 	AXP20X_IRQ(VBUS_PLUGIN,		0, 3),
82 	AXP20X_IRQ(VBUS_REMOVAL,	0, 2),
83 	AXP20X_IRQ(VBUS_V_LOW,		0, 1),
84 	AXP20X_IRQ(BATT_PLUGIN,		1, 7),
85 	AXP20X_IRQ(BATT_REMOVAL,	1, 6),
86 	AXP20X_IRQ(BATT_ENT_ACT_MODE,	1, 5),
87 	AXP20X_IRQ(BATT_EXIT_ACT_MODE,	1, 4),
88 	AXP20X_IRQ(CHARG,		1, 3),
89 	AXP20X_IRQ(CHARG_DONE,		1, 2),
90 	AXP20X_IRQ(BATT_TEMP_HIGH,	1, 1),
91 	AXP20X_IRQ(BATT_TEMP_LOW,	1, 0),
92 	AXP20X_IRQ(DIE_TEMP_HIGH,	2, 7),
93 	AXP20X_IRQ(CHARG_I_LOW,		2, 6),
94 	AXP20X_IRQ(DCDC1_V_LONG,	2, 5),
95 	AXP20X_IRQ(DCDC2_V_LONG,	2, 4),
96 	AXP20X_IRQ(DCDC3_V_LONG,	2, 3),
97 	AXP20X_IRQ(PEK_SHORT,		2, 1),
98 	AXP20X_IRQ(PEK_LONG,		2, 0),
99 	AXP20X_IRQ(N_OE_PWR_ON,		3, 7),
100 	AXP20X_IRQ(N_OE_PWR_OFF,	3, 6),
101 	AXP20X_IRQ(VBUS_VALID,		3, 5),
102 	AXP20X_IRQ(VBUS_NOT_VALID,	3, 4),
103 	AXP20X_IRQ(VBUS_SESS_VALID,	3, 3),
104 	AXP20X_IRQ(VBUS_SESS_END,	3, 2),
105 	AXP20X_IRQ(LOW_PWR_LVL1,	3, 1),
106 	AXP20X_IRQ(LOW_PWR_LVL2,	3, 0),
107 	AXP20X_IRQ(TIMER,		4, 7),
108 	AXP20X_IRQ(PEK_RIS_EDGE,	4, 6),
109 	AXP20X_IRQ(PEK_FAL_EDGE,	4, 5),
110 	AXP20X_IRQ(GPIO3_INPUT,		4, 3),
111 	AXP20X_IRQ(GPIO2_INPUT,		4, 2),
112 	AXP20X_IRQ(GPIO1_INPUT,		4, 1),
113 	AXP20X_IRQ(GPIO0_INPUT,		4, 0),
114 };
115 
116 static const struct of_device_id axp20x_of_match[] = {
117 	{ .compatible = "x-powers,axp202", .data = (void *) AXP202_ID },
118 	{ .compatible = "x-powers,axp209", .data = (void *) AXP209_ID },
119 	{ },
120 };
121 MODULE_DEVICE_TABLE(of, axp20x_of_match);
122 
123 /*
124  * This is useless for OF-enabled devices, but it is needed by I2C subsystem
125  */
126 static const struct i2c_device_id axp20x_i2c_id[] = {
127 	{ },
128 };
129 MODULE_DEVICE_TABLE(i2c, axp20x_i2c_id);
130 
131 static const struct regmap_irq_chip axp20x_regmap_irq_chip = {
132 	.name			= "axp20x_irq_chip",
133 	.status_base		= AXP20X_IRQ1_STATE,
134 	.ack_base		= AXP20X_IRQ1_STATE,
135 	.mask_base		= AXP20X_IRQ1_EN,
136 	.num_regs		= 5,
137 	.irqs			= axp20x_regmap_irqs,
138 	.num_irqs		= ARRAY_SIZE(axp20x_regmap_irqs),
139 	.mask_invert		= true,
140 	.init_ack_masked	= true,
141 };
142 
143 static struct mfd_cell axp20x_cells[] = {
144 	{
145 		.name			= "axp20x-pek",
146 		.num_resources		= ARRAY_SIZE(axp20x_pek_resources),
147 		.resources		= axp20x_pek_resources,
148 	}, {
149 		.name			= "axp20x-regulator",
150 	},
151 };
152 
153 static struct axp20x_dev *axp20x_pm_power_off;
154 static void axp20x_power_off(void)
155 {
156 	regmap_write(axp20x_pm_power_off->regmap, AXP20X_OFF_CTRL,
157 		     AXP20X_OFF);
158 }
159 
160 static int axp20x_i2c_probe(struct i2c_client *i2c,
161 			 const struct i2c_device_id *id)
162 {
163 	struct axp20x_dev *axp20x;
164 	const struct of_device_id *of_id;
165 	int ret;
166 
167 	axp20x = devm_kzalloc(&i2c->dev, sizeof(*axp20x), GFP_KERNEL);
168 	if (!axp20x)
169 		return -ENOMEM;
170 
171 	of_id = of_match_device(axp20x_of_match, &i2c->dev);
172 	if (!of_id) {
173 		dev_err(&i2c->dev, "Unable to setup AXP20X data\n");
174 		return -ENODEV;
175 	}
176 	axp20x->variant = (long) of_id->data;
177 
178 	axp20x->i2c_client = i2c;
179 	axp20x->dev = &i2c->dev;
180 	dev_set_drvdata(axp20x->dev, axp20x);
181 
182 	axp20x->regmap = devm_regmap_init_i2c(i2c, &axp20x_regmap_config);
183 	if (IS_ERR(axp20x->regmap)) {
184 		ret = PTR_ERR(axp20x->regmap);
185 		dev_err(&i2c->dev, "regmap init failed: %d\n", ret);
186 		return ret;
187 	}
188 
189 	ret = regmap_add_irq_chip(axp20x->regmap, i2c->irq,
190 				  IRQF_ONESHOT | IRQF_SHARED, -1,
191 				  &axp20x_regmap_irq_chip,
192 				  &axp20x->regmap_irqc);
193 	if (ret) {
194 		dev_err(&i2c->dev, "failed to add irq chip: %d\n", ret);
195 		return ret;
196 	}
197 
198 	ret = mfd_add_devices(axp20x->dev, -1, axp20x_cells,
199 			      ARRAY_SIZE(axp20x_cells), NULL, 0, NULL);
200 
201 	if (ret) {
202 		dev_err(&i2c->dev, "failed to add MFD devices: %d\n", ret);
203 		regmap_del_irq_chip(i2c->irq, axp20x->regmap_irqc);
204 		return ret;
205 	}
206 
207 	if (!pm_power_off) {
208 		axp20x_pm_power_off = axp20x;
209 		pm_power_off = axp20x_power_off;
210 	}
211 
212 	dev_info(&i2c->dev, "AXP20X driver loaded\n");
213 
214 	return 0;
215 }
216 
217 static int axp20x_i2c_remove(struct i2c_client *i2c)
218 {
219 	struct axp20x_dev *axp20x = i2c_get_clientdata(i2c);
220 
221 	if (axp20x == axp20x_pm_power_off) {
222 		axp20x_pm_power_off = NULL;
223 		pm_power_off = NULL;
224 	}
225 
226 	mfd_remove_devices(axp20x->dev);
227 	regmap_del_irq_chip(axp20x->i2c_client->irq, axp20x->regmap_irqc);
228 
229 	return 0;
230 }
231 
232 static struct i2c_driver axp20x_i2c_driver = {
233 	.driver = {
234 		.name	= "axp20x",
235 		.owner	= THIS_MODULE,
236 		.of_match_table	= of_match_ptr(axp20x_of_match),
237 	},
238 	.probe		= axp20x_i2c_probe,
239 	.remove		= axp20x_i2c_remove,
240 	.id_table	= axp20x_i2c_id,
241 };
242 
243 module_i2c_driver(axp20x_i2c_driver);
244 
245 MODULE_DESCRIPTION("PMIC MFD core driver for AXP20X");
246 MODULE_AUTHOR("Carlo Caione <carlo@caione.org>");
247 MODULE_LICENSE("GPL");
248