xref: /openbmc/linux/drivers/mfd/atc260x-core.c (revision dc0c386e)
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Core support for ATC260x PMICs
4  *
5  * Copyright (C) 2019 Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
6  * Copyright (C) 2020 Cristian Ciocaltea <cristian.ciocaltea@gmail.com>
7  */
8 
9 #include <linux/interrupt.h>
10 #include <linux/mfd/atc260x/core.h>
11 #include <linux/mfd/core.h>
12 #include <linux/module.h>
13 #include <linux/of.h>
14 #include <linux/regmap.h>
15 
16 #define ATC260X_CHIP_REV_MAX	31
17 
18 struct atc260x_init_regs {
19 	unsigned int cmu_devrst;
20 	unsigned int cmu_devrst_ints;
21 	unsigned int ints_msk;
22 	unsigned int pad_en;
23 	unsigned int pad_en_extirq;
24 };
25 
regmap_lock_mutex(void * __mutex)26 static void regmap_lock_mutex(void *__mutex)
27 {
28 	struct mutex *mutex = __mutex;
29 
30 	/*
31 	 * Using regmap within an atomic context (e.g. accessing a PMIC when
32 	 * powering system down) is normally allowed only if the regmap type
33 	 * is MMIO and the regcache type is either REGCACHE_NONE or
34 	 * REGCACHE_FLAT. For slow buses like I2C and SPI, the regmap is
35 	 * internally protected by a mutex which is acquired non-atomically.
36 	 *
37 	 * Let's improve this by using a customized locking scheme inspired
38 	 * from I2C atomic transfer. See i2c_in_atomic_xfer_mode() for a
39 	 * starting point.
40 	 */
41 	if (system_state > SYSTEM_RUNNING && irqs_disabled())
42 		mutex_trylock(mutex);
43 	else
44 		mutex_lock(mutex);
45 }
46 
regmap_unlock_mutex(void * __mutex)47 static void regmap_unlock_mutex(void *__mutex)
48 {
49 	struct mutex *mutex = __mutex;
50 
51 	mutex_unlock(mutex);
52 }
53 
54 static const struct regmap_config atc2603c_regmap_config = {
55 	.reg_bits = 8,
56 	.val_bits = 16,
57 	.max_register = ATC2603C_SADDR,
58 	.cache_type = REGCACHE_NONE,
59 };
60 
61 static const struct regmap_config atc2609a_regmap_config = {
62 	.reg_bits = 8,
63 	.val_bits = 16,
64 	.max_register = ATC2609A_SADDR,
65 	.cache_type = REGCACHE_NONE,
66 };
67 
68 static const struct regmap_irq atc2603c_regmap_irqs[] = {
69 	REGMAP_IRQ_REG(ATC2603C_IRQ_AUDIO,	0, ATC2603C_INTS_MSK_AUDIO),
70 	REGMAP_IRQ_REG(ATC2603C_IRQ_OV,		0, ATC2603C_INTS_MSK_OV),
71 	REGMAP_IRQ_REG(ATC2603C_IRQ_OC,		0, ATC2603C_INTS_MSK_OC),
72 	REGMAP_IRQ_REG(ATC2603C_IRQ_OT,		0, ATC2603C_INTS_MSK_OT),
73 	REGMAP_IRQ_REG(ATC2603C_IRQ_UV,		0, ATC2603C_INTS_MSK_UV),
74 	REGMAP_IRQ_REG(ATC2603C_IRQ_ALARM,	0, ATC2603C_INTS_MSK_ALARM),
75 	REGMAP_IRQ_REG(ATC2603C_IRQ_ONOFF,	0, ATC2603C_INTS_MSK_ONOFF),
76 	REGMAP_IRQ_REG(ATC2603C_IRQ_SGPIO,	0, ATC2603C_INTS_MSK_SGPIO),
77 	REGMAP_IRQ_REG(ATC2603C_IRQ_IR,		0, ATC2603C_INTS_MSK_IR),
78 	REGMAP_IRQ_REG(ATC2603C_IRQ_REMCON,	0, ATC2603C_INTS_MSK_REMCON),
79 	REGMAP_IRQ_REG(ATC2603C_IRQ_POWER_IN,	0, ATC2603C_INTS_MSK_POWERIN),
80 };
81 
82 static const struct regmap_irq atc2609a_regmap_irqs[] = {
83 	REGMAP_IRQ_REG(ATC2609A_IRQ_AUDIO,	0, ATC2609A_INTS_MSK_AUDIO),
84 	REGMAP_IRQ_REG(ATC2609A_IRQ_OV,		0, ATC2609A_INTS_MSK_OV),
85 	REGMAP_IRQ_REG(ATC2609A_IRQ_OC,		0, ATC2609A_INTS_MSK_OC),
86 	REGMAP_IRQ_REG(ATC2609A_IRQ_OT,		0, ATC2609A_INTS_MSK_OT),
87 	REGMAP_IRQ_REG(ATC2609A_IRQ_UV,		0, ATC2609A_INTS_MSK_UV),
88 	REGMAP_IRQ_REG(ATC2609A_IRQ_ALARM,	0, ATC2609A_INTS_MSK_ALARM),
89 	REGMAP_IRQ_REG(ATC2609A_IRQ_ONOFF,	0, ATC2609A_INTS_MSK_ONOFF),
90 	REGMAP_IRQ_REG(ATC2609A_IRQ_WKUP,	0, ATC2609A_INTS_MSK_WKUP),
91 	REGMAP_IRQ_REG(ATC2609A_IRQ_IR,		0, ATC2609A_INTS_MSK_IR),
92 	REGMAP_IRQ_REG(ATC2609A_IRQ_REMCON,	0, ATC2609A_INTS_MSK_REMCON),
93 	REGMAP_IRQ_REG(ATC2609A_IRQ_POWER_IN,	0, ATC2609A_INTS_MSK_POWERIN),
94 };
95 
96 static const struct regmap_irq_chip atc2603c_regmap_irq_chip = {
97 	.name = "atc2603c",
98 	.irqs = atc2603c_regmap_irqs,
99 	.num_irqs = ARRAY_SIZE(atc2603c_regmap_irqs),
100 	.num_regs = 1,
101 	.status_base = ATC2603C_INTS_PD,
102 	.unmask_base = ATC2603C_INTS_MSK,
103 };
104 
105 static const struct regmap_irq_chip atc2609a_regmap_irq_chip = {
106 	.name = "atc2609a",
107 	.irqs = atc2609a_regmap_irqs,
108 	.num_irqs = ARRAY_SIZE(atc2609a_regmap_irqs),
109 	.num_regs = 1,
110 	.status_base = ATC2609A_INTS_PD,
111 	.unmask_base = ATC2609A_INTS_MSK,
112 };
113 
114 static const struct resource atc2603c_onkey_resources[] = {
115 	DEFINE_RES_IRQ(ATC2603C_IRQ_ONOFF),
116 };
117 
118 static const struct resource atc2609a_onkey_resources[] = {
119 	DEFINE_RES_IRQ(ATC2609A_IRQ_ONOFF),
120 };
121 
122 static const struct mfd_cell atc2603c_mfd_cells[] = {
123 	{ .name = "atc260x-regulator" },
124 	{ .name = "atc260x-pwrc" },
125 	{
126 		.name = "atc260x-onkey",
127 		.num_resources = ARRAY_SIZE(atc2603c_onkey_resources),
128 		.resources = atc2603c_onkey_resources,
129 	},
130 };
131 
132 static const struct mfd_cell atc2609a_mfd_cells[] = {
133 	{ .name = "atc260x-regulator" },
134 	{ .name = "atc260x-pwrc" },
135 	{
136 		.name = "atc260x-onkey",
137 		.num_resources = ARRAY_SIZE(atc2609a_onkey_resources),
138 		.resources = atc2609a_onkey_resources,
139 	},
140 };
141 
142 static const struct atc260x_init_regs atc2603c_init_regs = {
143 	.cmu_devrst = ATC2603C_CMU_DEVRST,
144 	.cmu_devrst_ints = ATC2603C_CMU_DEVRST_INTS,
145 	.ints_msk = ATC2603C_INTS_MSK,
146 	.pad_en = ATC2603C_PAD_EN,
147 	.pad_en_extirq = ATC2603C_PAD_EN_EXTIRQ,
148 };
149 
150 static const struct atc260x_init_regs atc2609a_init_regs = {
151 	.cmu_devrst = ATC2609A_CMU_DEVRST,
152 	.cmu_devrst_ints = ATC2609A_CMU_DEVRST_INTS,
153 	.ints_msk = ATC2609A_INTS_MSK,
154 	.pad_en = ATC2609A_PAD_EN,
155 	.pad_en_extirq = ATC2609A_PAD_EN_EXTIRQ,
156 };
157 
atc260x_cmu_reset(struct atc260x * atc260x)158 static void atc260x_cmu_reset(struct atc260x *atc260x)
159 {
160 	const struct atc260x_init_regs *regs = atc260x->init_regs;
161 
162 	/* Assert reset */
163 	regmap_update_bits(atc260x->regmap, regs->cmu_devrst,
164 			   regs->cmu_devrst_ints, ~regs->cmu_devrst_ints);
165 
166 	/* De-assert reset */
167 	regmap_update_bits(atc260x->regmap, regs->cmu_devrst,
168 			   regs->cmu_devrst_ints, regs->cmu_devrst_ints);
169 }
170 
atc260x_dev_init(struct atc260x * atc260x)171 static void atc260x_dev_init(struct atc260x *atc260x)
172 {
173 	const struct atc260x_init_regs *regs = atc260x->init_regs;
174 
175 	/* Initialize interrupt block */
176 	atc260x_cmu_reset(atc260x);
177 
178 	/* Disable all interrupt sources */
179 	regmap_write(atc260x->regmap, regs->ints_msk, 0);
180 
181 	/* Enable EXTIRQ pad */
182 	regmap_update_bits(atc260x->regmap, regs->pad_en,
183 			   regs->pad_en_extirq, regs->pad_en_extirq);
184 }
185 
186 /**
187  * atc260x_match_device(): Setup ATC260x variant related fields
188  *
189  * @atc260x: ATC260x device to setup (.dev field must be set)
190  * @regmap_cfg: regmap config associated with this ATC260x device
191  *
192  * This lets the ATC260x core configure the MFD cells and register maps
193  * for later use.
194  */
atc260x_match_device(struct atc260x * atc260x,struct regmap_config * regmap_cfg)195 int atc260x_match_device(struct atc260x *atc260x, struct regmap_config *regmap_cfg)
196 {
197 	struct device *dev = atc260x->dev;
198 	const void *of_data;
199 
200 	of_data = of_device_get_match_data(dev);
201 	if (!of_data)
202 		return -ENODEV;
203 
204 	atc260x->ic_type = (unsigned long)of_data;
205 
206 	switch (atc260x->ic_type) {
207 	case ATC2603C:
208 		*regmap_cfg = atc2603c_regmap_config;
209 		atc260x->regmap_irq_chip = &atc2603c_regmap_irq_chip;
210 		atc260x->cells = atc2603c_mfd_cells;
211 		atc260x->nr_cells = ARRAY_SIZE(atc2603c_mfd_cells);
212 		atc260x->type_name = "atc2603c";
213 		atc260x->rev_reg = ATC2603C_CHIP_VER;
214 		atc260x->init_regs = &atc2603c_init_regs;
215 		break;
216 	case ATC2609A:
217 		*regmap_cfg = atc2609a_regmap_config;
218 		atc260x->regmap_irq_chip = &atc2609a_regmap_irq_chip;
219 		atc260x->cells = atc2609a_mfd_cells;
220 		atc260x->nr_cells = ARRAY_SIZE(atc2609a_mfd_cells);
221 		atc260x->type_name = "atc2609a";
222 		atc260x->rev_reg = ATC2609A_CHIP_VER;
223 		atc260x->init_regs = &atc2609a_init_regs;
224 		break;
225 	default:
226 		dev_err(dev, "Unsupported ATC260x device type: %u\n",
227 			atc260x->ic_type);
228 		return -EINVAL;
229 	}
230 
231 	atc260x->regmap_mutex = devm_kzalloc(dev, sizeof(*atc260x->regmap_mutex),
232 					     GFP_KERNEL);
233 	if (!atc260x->regmap_mutex)
234 		return -ENOMEM;
235 
236 	mutex_init(atc260x->regmap_mutex);
237 
238 	regmap_cfg->lock = regmap_lock_mutex,
239 	regmap_cfg->unlock = regmap_unlock_mutex,
240 	regmap_cfg->lock_arg = atc260x->regmap_mutex;
241 
242 	return 0;
243 }
244 EXPORT_SYMBOL_GPL(atc260x_match_device);
245 
246 /**
247  * atc260x_device_probe(): Probe a configured ATC260x device
248  *
249  * @atc260x: ATC260x device to probe (must be configured)
250  *
251  * This function lets the ATC260x core register the ATC260x MFD devices
252  * and IRQCHIP. The ATC260x device passed in must be fully configured
253  * with atc260x_match_device, its IRQ set, and regmap created.
254  */
atc260x_device_probe(struct atc260x * atc260x)255 int atc260x_device_probe(struct atc260x *atc260x)
256 {
257 	struct device *dev = atc260x->dev;
258 	unsigned int chip_rev;
259 	int ret;
260 
261 	if (!atc260x->irq) {
262 		dev_err(dev, "No interrupt support\n");
263 		return -EINVAL;
264 	}
265 
266 	/* Initialize the hardware */
267 	atc260x_dev_init(atc260x);
268 
269 	ret = regmap_read(atc260x->regmap, atc260x->rev_reg, &chip_rev);
270 	if (ret) {
271 		dev_err(dev, "Failed to get chip revision\n");
272 		return ret;
273 	}
274 
275 	if (chip_rev > ATC260X_CHIP_REV_MAX) {
276 		dev_err(dev, "Unknown chip revision: %u\n", chip_rev);
277 		return -EINVAL;
278 	}
279 
280 	atc260x->ic_ver = __ffs(chip_rev + 1U);
281 
282 	dev_info(dev, "Detected chip type %s rev.%c\n",
283 		 atc260x->type_name, 'A' + atc260x->ic_ver);
284 
285 	ret = devm_regmap_add_irq_chip(dev, atc260x->regmap, atc260x->irq, IRQF_ONESHOT,
286 				       -1, atc260x->regmap_irq_chip, &atc260x->irq_data);
287 	if (ret) {
288 		dev_err(dev, "Failed to add IRQ chip: %d\n", ret);
289 		return ret;
290 	}
291 
292 	ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE,
293 				   atc260x->cells, atc260x->nr_cells, NULL, 0,
294 				   regmap_irq_get_domain(atc260x->irq_data));
295 	if (ret) {
296 		dev_err(dev, "Failed to add child devices: %d\n", ret);
297 		regmap_del_irq_chip(atc260x->irq, atc260x->irq_data);
298 	}
299 
300 	return ret;
301 }
302 EXPORT_SYMBOL_GPL(atc260x_device_probe);
303 
304 MODULE_DESCRIPTION("ATC260x PMICs Core support");
305 MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>");
306 MODULE_AUTHOR("Cristian Ciocaltea <cristian.ciocaltea@gmail.com>");
307 MODULE_LICENSE("GPL");
308