Commit | Line | Data |
---|---|---|
4b5e9b39 | 1 | // SPDX-License-Identifier: GPL-2.0 |
64542b9f MCC |
2 | // |
3 | // Device driver for regulators in HISI PMIC IC | |
4 | // | |
5 | // Copyright (c) 2013 Linaro Ltd. | |
6 | // Copyright (c) 2011 Hisilicon. | |
7 | // | |
8 | // Copyright (c) 2020-2021 Huawei Technologies Co., Ltd | |
4524ac56 | 9 | |
4524ac56 | 10 | #include <linux/interrupt.h> |
6b946699 | 11 | #include <linux/irq.h> |
cf0f27b7 | 12 | #include <linux/mfd/core.h> |
6b946699 MCC |
13 | #include <linux/mfd/hi6421-spmi-pmic.h> |
14 | #include <linux/module.h> | |
4524ac56 | 15 | #include <linux/of_gpio.h> |
6b946699 MCC |
16 | #include <linux/platform_device.h> |
17 | #include <linux/slab.h> | |
4524ac56 | 18 | #include <linux/spmi.h> |
4524ac56 M |
19 | |
20 | /* 8-bit register offset in PMIC */ | |
21 | #define HISI_MASK_STATE 0xff | |
22 | ||
b240d014 MCC |
23 | #define HISI_IRQ_ARRAY 2 |
24 | #define HISI_IRQ_NUM (HISI_IRQ_ARRAY * 8) | |
25 | ||
26 | #define SOC_PMIC_IRQ_MASK_0_ADDR 0x0202 | |
27 | #define SOC_PMIC_IRQ0_ADDR 0x0212 | |
28 | ||
4524ac56 M |
29 | #define HISI_IRQ_KEY_NUM 0 |
30 | #define HISI_IRQ_KEY_VALUE 0xc0 | |
31 | #define HISI_IRQ_KEY_DOWN 7 | |
32 | #define HISI_IRQ_KEY_UP 6 | |
33 | ||
6b946699 | 34 | #define HISI_MASK_FIELD 0xFF |
4524ac56 | 35 | #define HISI_BITS 8 |
4524ac56 M |
36 | |
37 | /*define the first group interrupt register number*/ | |
6b946699 | 38 | #define HISI_PMIC_FIRST_GROUP_INT_NUM 2 |
4524ac56 | 39 | |
cf0f27b7 MCC |
40 | static const struct mfd_cell hi6421v600_devs[] = { |
41 | { .name = "hi6421v600-regulator", }, | |
4524ac56 M |
42 | }; |
43 | ||
fb02e3eb | 44 | static irqreturn_t hi6421_spmi_irq_handler(int irq, void *priv) |
4524ac56 | 45 | { |
fcd73240 | 46 | struct hi6421_spmi_pmic *ddata = (struct hi6421_spmi_pmic *)priv; |
4524ac56 | 47 | unsigned long pending; |
fb02e3eb | 48 | unsigned int data; |
4524ac56 M |
49 | int i, offset; |
50 | ||
b240d014 | 51 | for (i = 0; i < HISI_IRQ_ARRAY; i++) { |
fcd73240 | 52 | regmap_read(ddata->regmap, offset, &data); |
fb02e3eb MCC |
53 | data &= HISI_MASK_FIELD; |
54 | if (data != 0) | |
55 | pr_debug("data[%d]=0x%d\n\r", i, data); | |
fcd73240 | 56 | regmap_write(ddata->regmap, i + SOC_PMIC_IRQ0_ADDR, data); |
4524ac56 | 57 | |
fb02e3eb MCC |
58 | /* for_each_set_bit() macro requires unsigned long */ |
59 | pending = data; | |
4524ac56 | 60 | |
4b5e9b39 | 61 | /* solve powerkey order */ |
6b946699 MCC |
62 | if ((i == HISI_IRQ_KEY_NUM) && |
63 | ((pending & HISI_IRQ_KEY_VALUE) == HISI_IRQ_KEY_VALUE)) { | |
fcd73240 MCC |
64 | generic_handle_irq(ddata->irqs[HISI_IRQ_KEY_DOWN]); |
65 | generic_handle_irq(ddata->irqs[HISI_IRQ_KEY_UP]); | |
4524ac56 M |
66 | pending &= (~HISI_IRQ_KEY_VALUE); |
67 | } | |
68 | ||
69 | if (pending) { | |
70 | for_each_set_bit(offset, &pending, HISI_BITS) | |
fcd73240 | 71 | generic_handle_irq(ddata->irqs[offset + i * HISI_BITS]); |
4524ac56 M |
72 | } |
73 | } | |
74 | ||
4524ac56 M |
75 | return IRQ_HANDLED; |
76 | } | |
77 | ||
1eb2784a | 78 | static void hi6421_spmi_irq_mask(struct irq_data *d) |
4524ac56 | 79 | { |
fcd73240 | 80 | struct hi6421_spmi_pmic *ddata = irq_data_get_irq_chip_data(d); |
4524ac56 | 81 | unsigned long flags; |
fb02e3eb MCC |
82 | unsigned int data; |
83 | u32 offset; | |
4524ac56 | 84 | |
4524ac56 | 85 | offset = (irqd_to_hwirq(d) >> 3); |
b240d014 | 86 | offset += SOC_PMIC_IRQ_MASK_0_ADDR; |
bd07d62a | 87 | |
fcd73240 | 88 | spin_lock_irqsave(&ddata->lock, flags); |
fb02e3eb | 89 | |
fcd73240 | 90 | regmap_read(ddata->regmap, offset, &data); |
4524ac56 | 91 | data |= (1 << (irqd_to_hwirq(d) & 0x07)); |
fcd73240 MCC |
92 | regmap_write(ddata->regmap, offset, data); |
93 | spin_unlock_irqrestore(&ddata->lock, flags); | |
4524ac56 M |
94 | } |
95 | ||
1eb2784a | 96 | static void hi6421_spmi_irq_unmask(struct irq_data *d) |
4524ac56 | 97 | { |
fcd73240 | 98 | struct hi6421_spmi_pmic *ddata = irq_data_get_irq_chip_data(d); |
4524ac56 M |
99 | u32 data, offset; |
100 | unsigned long flags; | |
101 | ||
4524ac56 | 102 | offset = (irqd_to_hwirq(d) >> 3); |
b240d014 | 103 | offset += SOC_PMIC_IRQ_MASK_0_ADDR; |
bd07d62a | 104 | |
fcd73240 MCC |
105 | spin_lock_irqsave(&ddata->lock, flags); |
106 | regmap_read(ddata->regmap, offset, &data); | |
02a9bd4f | 107 | data &= ~(1 << (irqd_to_hwirq(d) & 0x07)); |
fcd73240 MCC |
108 | regmap_write(ddata->regmap, offset, data); |
109 | spin_unlock_irqrestore(&ddata->lock, flags); | |
4524ac56 M |
110 | } |
111 | ||
1eb2784a | 112 | static struct irq_chip hi6421_spmi_pmu_irqchip = { |
4524ac56 | 113 | .name = "hisi-irq", |
1eb2784a MCC |
114 | .irq_mask = hi6421_spmi_irq_mask, |
115 | .irq_unmask = hi6421_spmi_irq_unmask, | |
116 | .irq_disable = hi6421_spmi_irq_mask, | |
117 | .irq_enable = hi6421_spmi_irq_unmask, | |
4524ac56 M |
118 | }; |
119 | ||
1eb2784a | 120 | static int hi6421_spmi_irq_map(struct irq_domain *d, unsigned int virq, |
4d70881a | 121 | irq_hw_number_t hw) |
4524ac56 | 122 | { |
fcd73240 | 123 | struct hi6421_spmi_pmic *ddata = d->host_data; |
4524ac56 | 124 | |
1eb2784a | 125 | irq_set_chip_and_handler_name(virq, &hi6421_spmi_pmu_irqchip, |
4524ac56 | 126 | handle_simple_irq, "hisi"); |
fcd73240 | 127 | irq_set_chip_data(virq, ddata); |
4524ac56 M |
128 | irq_set_irq_type(virq, IRQ_TYPE_NONE); |
129 | ||
130 | return 0; | |
131 | } | |
132 | ||
1eb2784a MCC |
133 | static const struct irq_domain_ops hi6421_spmi_domain_ops = { |
134 | .map = hi6421_spmi_irq_map, | |
4524ac56 M |
135 | .xlate = irq_domain_xlate_twocell, |
136 | }; | |
137 | ||
fcd73240 | 138 | static void hi6421_spmi_pmic_irq_prc(struct hi6421_spmi_pmic *ddata) |
4524ac56 | 139 | { |
fb02e3eb MCC |
140 | int i; |
141 | unsigned int pending; | |
4b5e9b39 | 142 | |
b240d014 | 143 | for (i = 0 ; i < HISI_IRQ_ARRAY; i++) |
fcd73240 | 144 | regmap_write(ddata->regmap, SOC_PMIC_IRQ_MASK_0_ADDR + i, |
1eb2784a | 145 | HISI_MASK_STATE); |
4524ac56 | 146 | |
b240d014 | 147 | for (i = 0 ; i < HISI_IRQ_ARRAY; i++) { |
fcd73240 | 148 | regmap_read(ddata->regmap, SOC_PMIC_IRQ0_ADDR + i, &pending); |
4b5e9b39 MCC |
149 | |
150 | pr_debug("PMU IRQ address value:irq[0x%x] = 0x%x\n", | |
b240d014 | 151 | SOC_PMIC_IRQ0_ADDR + i, pending); |
fcd73240 | 152 | regmap_write(ddata->regmap, SOC_PMIC_IRQ0_ADDR + i, |
fb02e3eb | 153 | HISI_MASK_STATE); |
4524ac56 | 154 | } |
4524ac56 M |
155 | } |
156 | ||
fb02e3eb MCC |
157 | static const struct regmap_config spmi_regmap_config = { |
158 | .reg_bits = 16, | |
159 | .val_bits = 8, | |
160 | .max_register = 0xffff, | |
161 | .fast_io = true | |
162 | }; | |
163 | ||
1eb2784a | 164 | static int hi6421_spmi_pmic_probe(struct spmi_device *pdev) |
4524ac56 M |
165 | { |
166 | struct device *dev = &pdev->dev; | |
167 | struct device_node *np = dev->of_node; | |
fcd73240 | 168 | struct hi6421_spmi_pmic *ddata; |
fb02e3eb | 169 | struct regmap *map; |
4524ac56 | 170 | unsigned int virq; |
6b946699 | 171 | int ret, i; |
4524ac56 | 172 | |
fcd73240 MCC |
173 | ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL); |
174 | if (!ddata) | |
4524ac56 | 175 | return -ENOMEM; |
4524ac56 | 176 | |
fb02e3eb MCC |
177 | map = devm_regmap_init_spmi_ext(pdev, &spmi_regmap_config); |
178 | if (IS_ERR(map)) | |
179 | return PTR_ERR(map); | |
180 | ||
fcd73240 | 181 | spin_lock_init(&ddata->lock); |
4524ac56 | 182 | |
fcd73240 MCC |
183 | ddata->dev = dev; |
184 | ddata->regmap = map; | |
4524ac56 | 185 | |
fcd73240 MCC |
186 | ddata->gpio = of_get_gpio(np, 0); |
187 | if (ddata->gpio < 0) | |
188 | return ddata->gpio; | |
4524ac56 | 189 | |
fcd73240 | 190 | if (!gpio_is_valid(ddata->gpio)) |
4524ac56 M |
191 | return -EINVAL; |
192 | ||
fcd73240 | 193 | ret = devm_gpio_request_one(dev, ddata->gpio, GPIOF_IN, "pmic"); |
4524ac56 | 194 | if (ret < 0) { |
fcd73240 | 195 | dev_err(dev, "failed to request gpio%d\n", ddata->gpio); |
4524ac56 M |
196 | return ret; |
197 | } | |
198 | ||
fcd73240 | 199 | ddata->irq = gpio_to_irq(ddata->gpio); |
4524ac56 | 200 | |
fcd73240 | 201 | hi6421_spmi_pmic_irq_prc(ddata); |
4524ac56 | 202 | |
fcd73240 MCC |
203 | ddata->irqs = devm_kzalloc(dev, HISI_IRQ_NUM * sizeof(int), GFP_KERNEL); |
204 | if (!ddata->irqs) { | |
ba3e4a2a | 205 | ret = -ENOMEM; |
4524ac56 | 206 | goto irq_malloc; |
ba3e4a2a | 207 | } |
4524ac56 | 208 | |
fcd73240 MCC |
209 | ddata->domain = irq_domain_add_simple(np, HISI_IRQ_NUM, 0, |
210 | &hi6421_spmi_domain_ops, ddata); | |
211 | if (!ddata->domain) { | |
4524ac56 M |
212 | dev_err(dev, "failed irq domain add simple!\n"); |
213 | ret = -ENODEV; | |
6b946699 | 214 | goto irq_malloc; |
4524ac56 M |
215 | } |
216 | ||
b240d014 | 217 | for (i = 0; i < HISI_IRQ_NUM; i++) { |
fcd73240 | 218 | virq = irq_create_mapping(ddata->domain, i); |
6b946699 MCC |
219 | if (!virq) { |
220 | dev_err(dev, "Failed mapping hwirq\n"); | |
4524ac56 | 221 | ret = -ENOSPC; |
6b946699 | 222 | goto irq_malloc; |
4524ac56 | 223 | } |
fcd73240 MCC |
224 | ddata->irqs[i] = virq; |
225 | dev_dbg(dev, "%s: ddata->irqs[%d] = %d\n", | |
226 | __func__, i, ddata->irqs[i]); | |
4524ac56 M |
227 | } |
228 | ||
fcd73240 | 229 | ret = request_threaded_irq(ddata->irq, hi6421_spmi_irq_handler, NULL, |
4b5e9b39 | 230 | IRQF_TRIGGER_LOW | IRQF_SHARED | IRQF_NO_SUSPEND, |
fcd73240 | 231 | "pmic", ddata); |
4524ac56 | 232 | if (ret < 0) { |
6b946699 MCC |
233 | dev_err(dev, "could not claim pmic IRQ: error %d\n", ret); |
234 | goto irq_malloc; | |
4524ac56 M |
235 | } |
236 | ||
fcd73240 | 237 | dev_set_drvdata(&pdev->dev, ddata); |
cf0f27b7 MCC |
238 | |
239 | /* | |
fcd73240 | 240 | * The logic below will rely that the ddata is already stored at |
cf0f27b7 MCC |
241 | * drvdata. |
242 | */ | |
4d70881a | 243 | dev_dbg(&pdev->dev, "SPMI-PMIC: adding children for %pOF\n", |
cf0f27b7 MCC |
244 | pdev->dev.of_node); |
245 | ret = devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE, | |
246 | hi6421v600_devs, ARRAY_SIZE(hi6421v600_devs), | |
247 | NULL, 0, NULL); | |
6b946699 MCC |
248 | if (!ret) |
249 | return 0; | |
cf0f27b7 | 250 | |
6b946699 | 251 | dev_err(dev, "Failed to add child devices: %d\n", ret); |
4524ac56 | 252 | |
4524ac56 | 253 | irq_malloc: |
fcd73240 | 254 | free_irq(ddata->irq, ddata); |
6b946699 | 255 | |
4524ac56 M |
256 | return ret; |
257 | } | |
258 | ||
1eb2784a | 259 | static void hi6421_spmi_pmic_remove(struct spmi_device *pdev) |
4524ac56 | 260 | { |
fcd73240 | 261 | struct hi6421_spmi_pmic *ddata = dev_get_drvdata(&pdev->dev); |
4524ac56 | 262 | |
fcd73240 | 263 | free_irq(ddata->irq, ddata); |
4524ac56 | 264 | } |
4b5e9b39 | 265 | |
cf0f27b7 | 266 | static const struct of_device_id pmic_spmi_id_table[] = { |
9f46c343 | 267 | { .compatible = "hisilicon,hi6421-spmi" }, |
cf0f27b7 MCC |
268 | { } |
269 | }; | |
270 | MODULE_DEVICE_TABLE(of, pmic_spmi_id_table); | |
4524ac56 | 271 | |
1eb2784a | 272 | static struct spmi_driver hi6421_spmi_pmic_driver = { |
4524ac56 | 273 | .driver = { |
cf0f27b7 MCC |
274 | .name = "hi6421-spmi-pmic", |
275 | .of_match_table = pmic_spmi_id_table, | |
4524ac56 | 276 | }, |
1eb2784a MCC |
277 | .probe = hi6421_spmi_pmic_probe, |
278 | .remove = hi6421_spmi_pmic_remove, | |
4524ac56 | 279 | }; |
1eb2784a | 280 | module_spmi_driver(hi6421_spmi_pmic_driver); |
4524ac56 | 281 | |
cf0f27b7 | 282 | MODULE_DESCRIPTION("HiSilicon Hi6421v600 SPMI PMIC driver"); |
4524ac56 | 283 | MODULE_LICENSE("GPL v2"); |