xref: /openbmc/linux/drivers/mfd/axp20x.c (revision 41dc27e3)
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 const char * const axp20x_supplies[] = {
144 	"acin",
145 	"vin2",
146 	"vin3",
147 	"ldo24in",
148 	"ldo3in",
149 	"ldo5in",
150 };
151 
152 static struct mfd_cell axp20x_cells[] = {
153 	{
154 		.name			= "axp20x-pek",
155 		.num_resources		= ARRAY_SIZE(axp20x_pek_resources),
156 		.resources		= axp20x_pek_resources,
157 	}, {
158 		.name			= "axp20x-regulator",
159 		.parent_supplies	= axp20x_supplies,
160 		.num_parent_supplies	= ARRAY_SIZE(axp20x_supplies),
161 	},
162 };
163 
164 static struct axp20x_dev *axp20x_pm_power_off;
165 static void axp20x_power_off(void)
166 {
167 	regmap_write(axp20x_pm_power_off->regmap, AXP20X_OFF_CTRL,
168 		     AXP20X_OFF);
169 }
170 
171 static int axp20x_i2c_probe(struct i2c_client *i2c,
172 			 const struct i2c_device_id *id)
173 {
174 	struct axp20x_dev *axp20x;
175 	const struct of_device_id *of_id;
176 	int ret;
177 
178 	axp20x = devm_kzalloc(&i2c->dev, sizeof(*axp20x), GFP_KERNEL);
179 	if (!axp20x)
180 		return -ENOMEM;
181 
182 	of_id = of_match_device(axp20x_of_match, &i2c->dev);
183 	if (!of_id) {
184 		dev_err(&i2c->dev, "Unable to setup AXP20X data\n");
185 		return -ENODEV;
186 	}
187 	axp20x->variant = (long) of_id->data;
188 
189 	axp20x->i2c_client = i2c;
190 	axp20x->dev = &i2c->dev;
191 	dev_set_drvdata(axp20x->dev, axp20x);
192 
193 	axp20x->regmap = devm_regmap_init_i2c(i2c, &axp20x_regmap_config);
194 	if (IS_ERR(axp20x->regmap)) {
195 		ret = PTR_ERR(axp20x->regmap);
196 		dev_err(&i2c->dev, "regmap init failed: %d\n", ret);
197 		return ret;
198 	}
199 
200 	ret = regmap_add_irq_chip(axp20x->regmap, i2c->irq,
201 				  IRQF_ONESHOT | IRQF_SHARED, -1,
202 				  &axp20x_regmap_irq_chip,
203 				  &axp20x->regmap_irqc);
204 	if (ret) {
205 		dev_err(&i2c->dev, "failed to add irq chip: %d\n", ret);
206 		return ret;
207 	}
208 
209 	ret = mfd_add_devices(axp20x->dev, -1, axp20x_cells,
210 			      ARRAY_SIZE(axp20x_cells), NULL, 0, NULL);
211 
212 	if (ret) {
213 		dev_err(&i2c->dev, "failed to add MFD devices: %d\n", ret);
214 		regmap_del_irq_chip(i2c->irq, axp20x->regmap_irqc);
215 		return ret;
216 	}
217 
218 	if (!pm_power_off) {
219 		axp20x_pm_power_off = axp20x;
220 		pm_power_off = axp20x_power_off;
221 	}
222 
223 	dev_info(&i2c->dev, "AXP20X driver loaded\n");
224 
225 	return 0;
226 }
227 
228 static int axp20x_i2c_remove(struct i2c_client *i2c)
229 {
230 	struct axp20x_dev *axp20x = i2c_get_clientdata(i2c);
231 
232 	if (axp20x == axp20x_pm_power_off) {
233 		axp20x_pm_power_off = NULL;
234 		pm_power_off = NULL;
235 	}
236 
237 	mfd_remove_devices(axp20x->dev);
238 	regmap_del_irq_chip(axp20x->i2c_client->irq, axp20x->regmap_irqc);
239 
240 	return 0;
241 }
242 
243 static struct i2c_driver axp20x_i2c_driver = {
244 	.driver = {
245 		.name	= "axp20x",
246 		.owner	= THIS_MODULE,
247 		.of_match_table	= of_match_ptr(axp20x_of_match),
248 	},
249 	.probe		= axp20x_i2c_probe,
250 	.remove		= axp20x_i2c_remove,
251 	.id_table	= axp20x_i2c_id,
252 };
253 
254 module_i2c_driver(axp20x_i2c_driver);
255 
256 MODULE_DESCRIPTION("PMIC MFD core driver for AXP20X");
257 MODULE_AUTHOR("Carlo Caione <carlo@caione.org>");
258 MODULE_LICENSE("GPL");
259