OSDN Git Service

perf/x86/uncore: Correct the number of CHAs on EMR
[tomoyo/tomoyo-test1.git] / drivers / mfd / atc260x-core.c
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
19 struct 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
27 static 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
48 static void regmap_unlock_mutex(void *__mutex)
49 {
50         struct mutex *mutex = __mutex;
51
52         mutex_unlock(mutex);
53 }
54
55 static 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
62 static 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
69 static 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
83 static 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
97 static 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,
103         .unmask_base = ATC2603C_INTS_MSK,
104 };
105
106 static 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,
112         .unmask_base = ATC2609A_INTS_MSK,
113 };
114
115 static const struct resource atc2603c_onkey_resources[] = {
116         DEFINE_RES_IRQ(ATC2603C_IRQ_ONOFF),
117 };
118
119 static const struct resource atc2609a_onkey_resources[] = {
120         DEFINE_RES_IRQ(ATC2609A_IRQ_ONOFF),
121 };
122
123 static 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
133 static 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
143 static 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
151 static 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
159 static 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
172 static 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  */
196 int 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 }
245 EXPORT_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  */
256 int 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 }
303 EXPORT_SYMBOL_GPL(atc260x_device_probe);
304
305 MODULE_DESCRIPTION("ATC260x PMICs Core support");
306 MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>");
307 MODULE_AUTHOR("Cristian Ciocaltea <cristian.ciocaltea@gmail.com>");
308 MODULE_LICENSE("GPL");