Merge branch 'for-6.5/core' into for-linus
[linux-2.6-microblaze.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/i2c.h>
8 #include <linux/interrupt.h>
9 #include <linux/irq.h>
10 #include <linux/irqdomain.h>
11 #include <linux/module.h>
12 #include <linux/of_device.h>
13 #include <linux/of_platform.h>
14 #include <linux/pinctrl/consumer.h>
15 #include <linux/regmap.h>
16 #include <linux/slab.h>
17
18 #include <dt-bindings/mfd/qcom-pm8008.h>
19
20 #define I2C_INTR_STATUS_BASE            0x0550
21 #define INT_RT_STS_OFFSET               0x10
22 #define INT_SET_TYPE_OFFSET             0x11
23 #define INT_POL_HIGH_OFFSET             0x12
24 #define INT_POL_LOW_OFFSET              0x13
25 #define INT_LATCHED_CLR_OFFSET          0x14
26 #define INT_EN_SET_OFFSET               0x15
27 #define INT_EN_CLR_OFFSET               0x16
28 #define INT_LATCHED_STS_OFFSET          0x18
29
30 enum {
31         PM8008_MISC,
32         PM8008_TEMP_ALARM,
33         PM8008_GPIO1,
34         PM8008_GPIO2,
35         PM8008_NUM_PERIPHS,
36 };
37
38 #define PM8008_PERIPH_0_BASE    0x900
39 #define PM8008_PERIPH_1_BASE    0x2400
40 #define PM8008_PERIPH_2_BASE    0xC000
41 #define PM8008_PERIPH_3_BASE    0xC100
42
43 #define PM8008_TEMP_ALARM_ADDR  PM8008_PERIPH_1_BASE
44 #define PM8008_GPIO1_ADDR       PM8008_PERIPH_2_BASE
45 #define PM8008_GPIO2_ADDR       PM8008_PERIPH_3_BASE
46
47 enum {
48         SET_TYPE_INDEX,
49         POLARITY_HI_INDEX,
50         POLARITY_LO_INDEX,
51 };
52
53 static unsigned int pm8008_config_regs[] = {
54         INT_SET_TYPE_OFFSET,
55         INT_POL_HIGH_OFFSET,
56         INT_POL_LOW_OFFSET,
57 };
58
59 static struct regmap_irq pm8008_irqs[] = {
60         REGMAP_IRQ_REG(PM8008_IRQ_MISC_UVLO,    PM8008_MISC,    BIT(0)),
61         REGMAP_IRQ_REG(PM8008_IRQ_MISC_OVLO,    PM8008_MISC,    BIT(1)),
62         REGMAP_IRQ_REG(PM8008_IRQ_MISC_OTST2,   PM8008_MISC,    BIT(2)),
63         REGMAP_IRQ_REG(PM8008_IRQ_MISC_OTST3,   PM8008_MISC,    BIT(3)),
64         REGMAP_IRQ_REG(PM8008_IRQ_MISC_LDO_OCP, PM8008_MISC,    BIT(4)),
65         REGMAP_IRQ_REG(PM8008_IRQ_TEMP_ALARM,   PM8008_TEMP_ALARM, BIT(0)),
66         REGMAP_IRQ_REG(PM8008_IRQ_GPIO1,        PM8008_GPIO1,   BIT(0)),
67         REGMAP_IRQ_REG(PM8008_IRQ_GPIO2,        PM8008_GPIO2,   BIT(0)),
68 };
69
70 static const unsigned int pm8008_periph_base[] = {
71         PM8008_PERIPH_0_BASE,
72         PM8008_PERIPH_1_BASE,
73         PM8008_PERIPH_2_BASE,
74         PM8008_PERIPH_3_BASE,
75 };
76
77 static unsigned int pm8008_get_irq_reg(struct regmap_irq_chip_data *data,
78                                        unsigned int base, int index)
79 {
80         /* Simple linear addressing for the main status register */
81         if (base == I2C_INTR_STATUS_BASE)
82                 return base + index;
83
84         return pm8008_periph_base[index] + base;
85 }
86
87 static int pm8008_set_type_config(unsigned int **buf, unsigned int type,
88                                   const struct regmap_irq *irq_data, int idx,
89                                   void *irq_drv_data)
90 {
91         switch (type) {
92         case IRQ_TYPE_EDGE_FALLING:
93         case IRQ_TYPE_LEVEL_LOW:
94                 buf[POLARITY_HI_INDEX][idx] &= ~irq_data->mask;
95                 buf[POLARITY_LO_INDEX][idx] |= irq_data->mask;
96                 break;
97
98         case IRQ_TYPE_EDGE_RISING:
99         case IRQ_TYPE_LEVEL_HIGH:
100                 buf[POLARITY_HI_INDEX][idx] |= irq_data->mask;
101                 buf[POLARITY_LO_INDEX][idx] &= ~irq_data->mask;
102                 break;
103
104         case IRQ_TYPE_EDGE_BOTH:
105                 buf[POLARITY_HI_INDEX][idx] |= irq_data->mask;
106                 buf[POLARITY_LO_INDEX][idx] |= irq_data->mask;
107                 break;
108
109         default:
110                 return -EINVAL;
111         }
112
113         if (type & IRQ_TYPE_EDGE_BOTH)
114                 buf[SET_TYPE_INDEX][idx] |= irq_data->mask;
115         else
116                 buf[SET_TYPE_INDEX][idx] &= ~irq_data->mask;
117
118         return 0;
119 }
120
121 static struct regmap_irq_chip pm8008_irq_chip = {
122         .name                   = "pm8008_irq",
123         .main_status            = I2C_INTR_STATUS_BASE,
124         .num_main_regs          = 1,
125         .irqs                   = pm8008_irqs,
126         .num_irqs               = ARRAY_SIZE(pm8008_irqs),
127         .num_regs               = PM8008_NUM_PERIPHS,
128         .status_base            = INT_LATCHED_STS_OFFSET,
129         .mask_base              = INT_EN_CLR_OFFSET,
130         .unmask_base            = INT_EN_SET_OFFSET,
131         .mask_unmask_non_inverted = true,
132         .ack_base               = INT_LATCHED_CLR_OFFSET,
133         .config_base            = pm8008_config_regs,
134         .num_config_bases       = ARRAY_SIZE(pm8008_config_regs),
135         .num_config_regs        = PM8008_NUM_PERIPHS,
136         .set_type_config        = pm8008_set_type_config,
137         .get_irq_reg            = pm8008_get_irq_reg,
138 };
139
140 static struct regmap_config qcom_mfd_regmap_cfg = {
141         .reg_bits       = 16,
142         .val_bits       = 8,
143         .max_register   = 0xFFFF,
144 };
145
146 static int pm8008_probe_irq_peripherals(struct device *dev,
147                                         struct regmap *regmap,
148                                         int client_irq)
149 {
150         int rc, i;
151         struct regmap_irq_type *type;
152         struct regmap_irq_chip_data *irq_data;
153
154         for (i = 0; i < ARRAY_SIZE(pm8008_irqs); i++) {
155                 type = &pm8008_irqs[i].type;
156
157                 type->type_reg_offset = pm8008_irqs[i].reg_offset;
158
159                 if (type->type_reg_offset == PM8008_MISC)
160                         type->types_supported = IRQ_TYPE_EDGE_RISING;
161                 else
162                         type->types_supported = (IRQ_TYPE_EDGE_BOTH |
163                                 IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW);
164         }
165
166         rc = devm_regmap_add_irq_chip(dev, regmap, client_irq,
167                         IRQF_SHARED, 0, &pm8008_irq_chip, &irq_data);
168         if (rc) {
169                 dev_err(dev, "Failed to add IRQ chip: %d\n", rc);
170                 return rc;
171         }
172
173         return 0;
174 }
175
176 static int pm8008_probe(struct i2c_client *client)
177 {
178         int rc;
179         struct device *dev;
180         struct regmap *regmap;
181
182         dev = &client->dev;
183         regmap = devm_regmap_init_i2c(client, &qcom_mfd_regmap_cfg);
184         if (IS_ERR(regmap))
185                 return PTR_ERR(regmap);
186
187         i2c_set_clientdata(client, regmap);
188
189         if (of_property_read_bool(dev->of_node, "interrupt-controller")) {
190                 rc = pm8008_probe_irq_peripherals(dev, regmap, client->irq);
191                 if (rc)
192                         dev_err(dev, "Failed to probe irq periphs: %d\n", rc);
193         }
194
195         return devm_of_platform_populate(dev);
196 }
197
198 static const struct of_device_id pm8008_match[] = {
199         { .compatible = "qcom,pm8008", },
200         { },
201 };
202
203 static struct i2c_driver pm8008_mfd_driver = {
204         .driver = {
205                 .name = "pm8008",
206                 .of_match_table = pm8008_match,
207         },
208         .probe_new = pm8008_probe,
209 };
210 module_i2c_driver(pm8008_mfd_driver);
211
212 MODULE_LICENSE("GPL v2");
213 MODULE_ALIAS("i2c:qcom-pm8008");