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