1 // SPDX-License-Identifier: GPL-2.0+
3 * Core support for ATC260x PMICs
5 * Copyright (C) 2019 Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
6 * Copyright (C) 2020 Cristian Ciocaltea <cristian.ciocaltea@gmail.com>
9 #include <linux/interrupt.h>
10 #include <linux/mfd/atc260x/core.h>
11 #include <linux/mfd/core.h>
12 #include <linux/module.h>
14 #include <linux/regmap.h>
16 #define ATC260X_CHIP_REV_MAX 31
18 struct atc260x_init_regs
{
19 unsigned int cmu_devrst
;
20 unsigned int cmu_devrst_ints
;
21 unsigned int ints_msk
;
23 unsigned int pad_en_extirq
;
26 static void regmap_lock_mutex(void *__mutex
)
28 struct mutex
*mutex
= __mutex
;
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.
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
41 if (system_state
> SYSTEM_RUNNING
&& irqs_disabled())
47 static void regmap_unlock_mutex(void *__mutex
)
49 struct mutex
*mutex
= __mutex
;
54 static const struct regmap_config atc2603c_regmap_config
= {
57 .max_register
= ATC2603C_SADDR
,
58 .cache_type
= REGCACHE_NONE
,
61 static const struct regmap_config atc2609a_regmap_config
= {
64 .max_register
= ATC2609A_SADDR
,
65 .cache_type
= REGCACHE_NONE
,
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
),
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
),
96 static const struct regmap_irq_chip atc2603c_regmap_irq_chip
= {
98 .irqs
= atc2603c_regmap_irqs
,
99 .num_irqs
= ARRAY_SIZE(atc2603c_regmap_irqs
),
101 .status_base
= ATC2603C_INTS_PD
,
102 .unmask_base
= ATC2603C_INTS_MSK
,
105 static const struct regmap_irq_chip atc2609a_regmap_irq_chip
= {
107 .irqs
= atc2609a_regmap_irqs
,
108 .num_irqs
= ARRAY_SIZE(atc2609a_regmap_irqs
),
110 .status_base
= ATC2609A_INTS_PD
,
111 .unmask_base
= ATC2609A_INTS_MSK
,
114 static const struct resource atc2603c_onkey_resources
[] = {
115 DEFINE_RES_IRQ(ATC2603C_IRQ_ONOFF
),
118 static const struct resource atc2609a_onkey_resources
[] = {
119 DEFINE_RES_IRQ(ATC2609A_IRQ_ONOFF
),
122 static const struct mfd_cell atc2603c_mfd_cells
[] = {
123 { .name
= "atc260x-regulator" },
124 { .name
= "atc260x-pwrc" },
126 .name
= "atc260x-onkey",
127 .num_resources
= ARRAY_SIZE(atc2603c_onkey_resources
),
128 .resources
= atc2603c_onkey_resources
,
132 static const struct mfd_cell atc2609a_mfd_cells
[] = {
133 { .name
= "atc260x-regulator" },
134 { .name
= "atc260x-pwrc" },
136 .name
= "atc260x-onkey",
137 .num_resources
= ARRAY_SIZE(atc2609a_onkey_resources
),
138 .resources
= atc2609a_onkey_resources
,
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
,
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
,
158 static void atc260x_cmu_reset(struct atc260x
*atc260x
)
160 const struct atc260x_init_regs
*regs
= atc260x
->init_regs
;
163 regmap_update_bits(atc260x
->regmap
, regs
->cmu_devrst
,
164 regs
->cmu_devrst_ints
, ~regs
->cmu_devrst_ints
);
166 /* De-assert reset */
167 regmap_update_bits(atc260x
->regmap
, regs
->cmu_devrst
,
168 regs
->cmu_devrst_ints
, regs
->cmu_devrst_ints
);
171 static void atc260x_dev_init(struct atc260x
*atc260x
)
173 const struct atc260x_init_regs
*regs
= atc260x
->init_regs
;
175 /* Initialize interrupt block */
176 atc260x_cmu_reset(atc260x
);
178 /* Disable all interrupt sources */
179 regmap_write(atc260x
->regmap
, regs
->ints_msk
, 0);
181 /* Enable EXTIRQ pad */
182 regmap_update_bits(atc260x
->regmap
, regs
->pad_en
,
183 regs
->pad_en_extirq
, regs
->pad_en_extirq
);
187 * atc260x_match_device(): Setup ATC260x variant related fields
189 * @atc260x: ATC260x device to setup (.dev field must be set)
190 * @regmap_cfg: regmap config associated with this ATC260x device
192 * This lets the ATC260x core configure the MFD cells and register maps
195 int atc260x_match_device(struct atc260x
*atc260x
, struct regmap_config
*regmap_cfg
)
197 struct device
*dev
= atc260x
->dev
;
200 of_data
= of_device_get_match_data(dev
);
204 atc260x
->ic_type
= (unsigned long)of_data
;
206 switch (atc260x
->ic_type
) {
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
;
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
;
226 dev_err(dev
, "Unsupported ATC260x device type: %u\n",
231 atc260x
->regmap_mutex
= devm_kzalloc(dev
, sizeof(*atc260x
->regmap_mutex
),
233 if (!atc260x
->regmap_mutex
)
236 mutex_init(atc260x
->regmap_mutex
);
238 regmap_cfg
->lock
= regmap_lock_mutex
;
239 regmap_cfg
->unlock
= regmap_unlock_mutex
;
240 regmap_cfg
->lock_arg
= atc260x
->regmap_mutex
;
244 EXPORT_SYMBOL_GPL(atc260x_match_device
);
247 * atc260x_device_probe(): Probe a configured ATC260x device
249 * @atc260x: ATC260x device to probe (must be configured)
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.
255 int atc260x_device_probe(struct atc260x
*atc260x
)
257 struct device
*dev
= atc260x
->dev
;
258 unsigned int chip_rev
;
262 dev_err(dev
, "No interrupt support\n");
266 /* Initialize the hardware */
267 atc260x_dev_init(atc260x
);
269 ret
= regmap_read(atc260x
->regmap
, atc260x
->rev_reg
, &chip_rev
);
271 dev_err(dev
, "Failed to get chip revision\n");
275 if (chip_rev
> ATC260X_CHIP_REV_MAX
) {
276 dev_err(dev
, "Unknown chip revision: %u\n", chip_rev
);
280 atc260x
->ic_ver
= __ffs(chip_rev
+ 1U);
282 dev_info(dev
, "Detected chip type %s rev.%c\n",
283 atc260x
->type_name
, 'A' + atc260x
->ic_ver
);
285 ret
= devm_regmap_add_irq_chip(dev
, atc260x
->regmap
, atc260x
->irq
, IRQF_ONESHOT
,
286 -1, atc260x
->regmap_irq_chip
, &atc260x
->irq_data
);
288 dev_err(dev
, "Failed to add IRQ chip: %d\n", ret
);
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
));
296 dev_err(dev
, "Failed to add child devices: %d\n", ret
);
297 regmap_del_irq_chip(atc260x
->irq
, atc260x
->irq_data
);
302 EXPORT_SYMBOL_GPL(atc260x_device_probe
);
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");