]> Git Repo - linux.git/blob - drivers/mfd/qcom-pm8008.c
crypto: akcipher - Drop sign/verify operations
[linux.git] / drivers / mfd / qcom-pm8008.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (c) 2021, The Linux Foundation. All rights reserved.
4  */
5
6 #include <linux/bitops.h>
7 #include <linux/gpio/consumer.h>
8 #include <linux/i2c.h>
9 #include <linux/interrupt.h>
10 #include <linux/ioport.h>
11 #include <linux/irq.h>
12 #include <linux/irqdomain.h>
13 #include <linux/mfd/core.h>
14 #include <linux/module.h>
15 #include <linux/of.h>
16 #include <linux/of_platform.h>
17 #include <linux/pinctrl/consumer.h>
18 #include <linux/regmap.h>
19 #include <linux/slab.h>
20
21 #define I2C_INTR_STATUS_BASE            0x0550
22 #define INT_RT_STS_OFFSET               0x10
23 #define INT_SET_TYPE_OFFSET             0x11
24 #define INT_POL_HIGH_OFFSET             0x12
25 #define INT_POL_LOW_OFFSET              0x13
26 #define INT_LATCHED_CLR_OFFSET          0x14
27 #define INT_EN_SET_OFFSET               0x15
28 #define INT_EN_CLR_OFFSET               0x16
29 #define INT_LATCHED_STS_OFFSET          0x18
30
31 enum {
32         PM8008_MISC,
33         PM8008_TEMP_ALARM,
34         PM8008_GPIO1,
35         PM8008_GPIO2,
36         PM8008_NUM_PERIPHS,
37 };
38
39 #define PM8008_PERIPH_0_BASE    0x900
40 #define PM8008_PERIPH_1_BASE    0x2400
41 #define PM8008_PERIPH_2_BASE    0xc000
42 #define PM8008_PERIPH_3_BASE    0xc100
43
44 #define PM8008_TEMP_ALARM_ADDR  PM8008_PERIPH_1_BASE
45 #define PM8008_GPIO1_ADDR       PM8008_PERIPH_2_BASE
46 #define PM8008_GPIO2_ADDR       PM8008_PERIPH_3_BASE
47
48 /* PM8008 IRQ numbers */
49 #define PM8008_IRQ_MISC_UVLO    0
50 #define PM8008_IRQ_MISC_OVLO    1
51 #define PM8008_IRQ_MISC_OTST2   2
52 #define PM8008_IRQ_MISC_OTST3   3
53 #define PM8008_IRQ_MISC_LDO_OCP 4
54 #define PM8008_IRQ_TEMP_ALARM   5
55 #define PM8008_IRQ_GPIO1        6
56 #define PM8008_IRQ_GPIO2        7
57
58 enum {
59         SET_TYPE_INDEX,
60         POLARITY_HI_INDEX,
61         POLARITY_LO_INDEX,
62 };
63
64 static const unsigned int pm8008_config_regs[] = {
65         INT_SET_TYPE_OFFSET,
66         INT_POL_HIGH_OFFSET,
67         INT_POL_LOW_OFFSET,
68 };
69
70 #define _IRQ(_irq, _off, _mask, _types)                 \
71         [_irq] = {                                      \
72                 .reg_offset = (_off),                   \
73                 .mask = (_mask),                        \
74                 .type = {                               \
75                         .type_reg_offset = (_off),      \
76                         .types_supported = (_types),    \
77                 },                                      \
78         }
79
80 static const struct regmap_irq pm8008_irqs[] = {
81         _IRQ(PM8008_IRQ_MISC_UVLO,    PM8008_MISC,      BIT(0), IRQ_TYPE_EDGE_RISING),
82         _IRQ(PM8008_IRQ_MISC_OVLO,    PM8008_MISC,      BIT(1), IRQ_TYPE_EDGE_RISING),
83         _IRQ(PM8008_IRQ_MISC_OTST2,   PM8008_MISC,      BIT(2), IRQ_TYPE_EDGE_RISING),
84         _IRQ(PM8008_IRQ_MISC_OTST3,   PM8008_MISC,      BIT(3), IRQ_TYPE_EDGE_RISING),
85         _IRQ(PM8008_IRQ_MISC_LDO_OCP, PM8008_MISC,      BIT(4), IRQ_TYPE_EDGE_RISING),
86         _IRQ(PM8008_IRQ_TEMP_ALARM,   PM8008_TEMP_ALARM,BIT(0), IRQ_TYPE_SENSE_MASK),
87         _IRQ(PM8008_IRQ_GPIO1,        PM8008_GPIO1,     BIT(0), IRQ_TYPE_SENSE_MASK),
88         _IRQ(PM8008_IRQ_GPIO2,        PM8008_GPIO2,     BIT(0), IRQ_TYPE_SENSE_MASK),
89 };
90
91 static const unsigned int pm8008_periph_base[] = {
92         PM8008_PERIPH_0_BASE,
93         PM8008_PERIPH_1_BASE,
94         PM8008_PERIPH_2_BASE,
95         PM8008_PERIPH_3_BASE,
96 };
97
98 static unsigned int pm8008_get_irq_reg(struct regmap_irq_chip_data *data,
99                                        unsigned int base, int index)
100 {
101         /* Simple linear addressing for the main status register */
102         if (base == I2C_INTR_STATUS_BASE)
103                 return base + index;
104
105         return pm8008_periph_base[index] + base;
106 }
107
108 static int pm8008_set_type_config(unsigned int **buf, unsigned int type,
109                                   const struct regmap_irq *irq_data, int idx,
110                                   void *irq_drv_data)
111 {
112         switch (type) {
113         case IRQ_TYPE_EDGE_FALLING:
114         case IRQ_TYPE_LEVEL_LOW:
115                 buf[POLARITY_HI_INDEX][idx] &= ~irq_data->mask;
116                 buf[POLARITY_LO_INDEX][idx] |= irq_data->mask;
117                 break;
118
119         case IRQ_TYPE_EDGE_RISING:
120         case IRQ_TYPE_LEVEL_HIGH:
121                 buf[POLARITY_HI_INDEX][idx] |= irq_data->mask;
122                 buf[POLARITY_LO_INDEX][idx] &= ~irq_data->mask;
123                 break;
124
125         case IRQ_TYPE_EDGE_BOTH:
126                 buf[POLARITY_HI_INDEX][idx] |= irq_data->mask;
127                 buf[POLARITY_LO_INDEX][idx] |= irq_data->mask;
128                 break;
129
130         default:
131                 return -EINVAL;
132         }
133
134         if (type & IRQ_TYPE_EDGE_BOTH)
135                 buf[SET_TYPE_INDEX][idx] |= irq_data->mask;
136         else
137                 buf[SET_TYPE_INDEX][idx] &= ~irq_data->mask;
138
139         return 0;
140 }
141
142 static const struct regmap_irq_chip pm8008_irq_chip = {
143         .name                   = "pm8008",
144         .main_status            = I2C_INTR_STATUS_BASE,
145         .num_main_regs          = 1,
146         .irqs                   = pm8008_irqs,
147         .num_irqs               = ARRAY_SIZE(pm8008_irqs),
148         .num_regs               = PM8008_NUM_PERIPHS,
149         .status_base            = INT_LATCHED_STS_OFFSET,
150         .mask_base              = INT_EN_CLR_OFFSET,
151         .unmask_base            = INT_EN_SET_OFFSET,
152         .mask_unmask_non_inverted = true,
153         .ack_base               = INT_LATCHED_CLR_OFFSET,
154         .config_base            = pm8008_config_regs,
155         .num_config_bases       = ARRAY_SIZE(pm8008_config_regs),
156         .num_config_regs        = PM8008_NUM_PERIPHS,
157         .set_type_config        = pm8008_set_type_config,
158         .get_irq_reg            = pm8008_get_irq_reg,
159 };
160
161 static const struct regmap_config qcom_mfd_regmap_cfg = {
162         .name           = "primary",
163         .reg_bits       = 16,
164         .val_bits       = 8,
165         .max_register   = 0xffff,
166 };
167
168 static const struct regmap_config pm8008_regmap_cfg_2 = {
169         .name           = "secondary",
170         .reg_bits       = 16,
171         .val_bits       = 8,
172         .max_register   = 0xffff,
173 };
174
175 static const struct resource pm8008_temp_res[] = {
176         DEFINE_RES_MEM(PM8008_TEMP_ALARM_ADDR, 0x100),
177         DEFINE_RES_IRQ(PM8008_IRQ_TEMP_ALARM),
178 };
179
180 static const struct mfd_cell pm8008_cells[] = {
181         MFD_CELL_NAME("pm8008-regulator"),
182         MFD_CELL_RES("qpnp-temp-alarm", pm8008_temp_res),
183         MFD_CELL_NAME("pm8008-gpio"),
184 };
185
186 static void devm_irq_domain_fwnode_release(void *data)
187 {
188         struct fwnode_handle *fwnode = data;
189
190         irq_domain_free_fwnode(fwnode);
191 }
192
193 static int pm8008_probe(struct i2c_client *client)
194 {
195         struct regmap_irq_chip_data *irq_data;
196         struct device *dev = &client->dev;
197         struct regmap *regmap, *regmap2;
198         struct fwnode_handle *fwnode;
199         struct i2c_client *dummy;
200         struct gpio_desc *reset;
201         char *name;
202         int ret;
203
204         dummy = devm_i2c_new_dummy_device(dev, client->adapter, client->addr + 1);
205         if (IS_ERR(dummy)) {
206                 ret = PTR_ERR(dummy);
207                 dev_err(dev, "failed to claim second address: %d\n", ret);
208                 return ret;
209         }
210
211         regmap2 = devm_regmap_init_i2c(dummy, &qcom_mfd_regmap_cfg);
212         if (IS_ERR(regmap2))
213                 return PTR_ERR(regmap2);
214
215         ret = regmap_attach_dev(dev, regmap2, &pm8008_regmap_cfg_2);
216         if (ret)
217                 return ret;
218
219         /* Default regmap must be attached last. */
220         regmap = devm_regmap_init_i2c(client, &qcom_mfd_regmap_cfg);
221         if (IS_ERR(regmap))
222                 return PTR_ERR(regmap);
223
224         reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
225         if (IS_ERR(reset))
226                 return PTR_ERR(reset);
227
228         /*
229          * The PMIC does not appear to require a post-reset delay, but wait
230          * for a millisecond for now anyway.
231          */
232         usleep_range(1000, 2000);
233
234         name = devm_kasprintf(dev, GFP_KERNEL, "%pOF-internal", dev->of_node);
235         if (!name)
236                 return -ENOMEM;
237
238         name = strreplace(name, '/', ':');
239
240         fwnode = irq_domain_alloc_named_fwnode(name);
241         if (!fwnode)
242                 return -ENOMEM;
243
244         ret = devm_add_action_or_reset(dev, devm_irq_domain_fwnode_release, fwnode);
245         if (ret)
246                 return ret;
247
248         ret = devm_regmap_add_irq_chip_fwnode(dev, fwnode, regmap, client->irq,
249                                 IRQF_SHARED, 0, &pm8008_irq_chip, &irq_data);
250         if (ret) {
251                 dev_err(dev, "failed to add IRQ chip: %d\n", ret);
252                 return ret;
253         }
254
255         /* Needed by GPIO driver. */
256         dev_set_drvdata(dev, regmap_irq_get_domain(irq_data));
257
258         return devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, pm8008_cells,
259                                 ARRAY_SIZE(pm8008_cells), NULL, 0,
260                                 regmap_irq_get_domain(irq_data));
261 }
262
263 static const struct of_device_id pm8008_match[] = {
264         { .compatible = "qcom,pm8008", },
265         { },
266 };
267 MODULE_DEVICE_TABLE(of, pm8008_match);
268
269 static struct i2c_driver pm8008_mfd_driver = {
270         .driver = {
271                 .name = "pm8008",
272                 .of_match_table = pm8008_match,
273         },
274         .probe = pm8008_probe,
275 };
276 module_i2c_driver(pm8008_mfd_driver);
277
278 MODULE_DESCRIPTION("QCOM PM8008 Power Management IC driver");
279 MODULE_LICENSE("GPL v2");
This page took 0.047981 seconds and 4 git commands to generate.