]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - drivers/gpio/gpio-qcom-smsm.c
Merge tag 'v4.4.9' into release/qcomlt-4.4
[karo-tx-linux.git] / drivers / gpio / gpio-qcom-smsm.c
1 /*
2  * Copyright (c) 2014, Sony Mobile Communications AB.
3  * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 and
7  * only version 2 as published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  */
14
15 #include <linux/module.h>
16 #include <linux/platform_device.h>
17 #include <linux/of_platform.h>
18 #include <linux/io.h>
19 #include <linux/interrupt.h>
20 #include <linux/memblock.h>
21 #include <linux/slab.h>
22 #include <linux/of_address.h>
23 #include <linux/of_irq.h>
24 #include <linux/hwspinlock.h>
25 #include <linux/regmap.h>
26 #include <linux/gpio.h>
27 #include <linux/mfd/syscon.h>
28
29 #include <linux/delay.h>
30
31 #include <linux/soc/qcom/smem.h>
32
33 #define SMSM_APPS_STATE 0
34 #define SMEM_SMSM_SHARED_STATE 85
35
36 #define SMSM_MAX_STATES 8
37
38 struct qcom_smsm_state {
39         unsigned state_id;
40         struct gpio_chip chip;
41
42         int irq;
43
44         struct regmap *ipc_regmap;
45         int ipc_bit;
46         int ipc_offset;
47 };
48
49 struct qcom_smsm {
50         struct device *dev;
51
52         u32 *shared_state;
53         size_t shared_state_size;
54
55         struct qcom_smsm_state states[SMSM_MAX_STATES];
56 };
57
58 #if 0
59 int qcom_smsm_change_state(struct qcom_smsm *smsm, u32 clear_mask, u32 set_mask)
60 {
61         u32 state;
62
63         dev_dbg(smsm->dev, "SMSM_APPS_STATE clear 0x%x set 0x%x\n", clear_mask, set_mask);
64         print_hex_dump(KERN_DEBUG, "raw data: ", DUMP_PREFIX_OFFSET, 16, 1, smsm->shared_state, smsm->shared_state_size, true);
65
66         state = readl(&smsm->shared_state[SMSM_APPS_STATE]);
67         state &= ~clear_mask;
68         state |= set_mask;
69         writel(state, &smsm->shared_state[SMSM_APPS_STATE]);
70
71         print_hex_dump(KERN_DEBUG, "raw data: ", DUMP_PREFIX_OFFSET, 16, 1, smsm->shared_state, smsm->shared_state_size, true);
72
73         // qcom_smem_signal(-1, smsm->smem, smsm->signal_offset, smsm->signal_bit);
74
75         return 0;
76 }
77 EXPORT_SYMBOL(qcom_smsm_change_state);
78 #endif
79
80 static struct qcom_smsm_state *to_smsm_state(struct gpio_chip *chip)
81 {
82         return container_of(chip, struct qcom_smsm_state, chip);
83 }
84
85 static struct qcom_smsm *to_smsm(struct qcom_smsm_state *state)
86 {
87         return container_of(state, struct qcom_smsm, states[state->state_id]);
88 }
89
90 static int smsm_gpio_direction_input(struct gpio_chip *chip,
91                                      unsigned offset)
92 {
93         struct qcom_smsm_state *state = to_smsm_state(chip);
94
95         if (state->state_id == SMSM_APPS_STATE)
96                 return -EINVAL;
97         return 0;
98 }
99
100 static int smsm_gpio_direction_output(struct gpio_chip *chip,
101                                       unsigned offset,
102                                       int value)
103 {
104         struct qcom_smsm_state *ipc_state;
105         struct qcom_smsm_state *state = to_smsm_state(chip);
106         struct qcom_smsm *smsm = to_smsm(state);
107         unsigned word;
108         unsigned bit;
109         u32 val;
110         int i;
111
112         /* Only SMSM_APPS_STATE supports writing */
113         if (state->state_id != SMSM_APPS_STATE)
114                 return -EINVAL;
115
116         offset += state->state_id * 32;
117
118         word = ALIGN(offset / 32, 4);
119         bit = offset % 32;
120
121         val = readl(smsm->shared_state + word);
122         if (value)
123                 val |= BIT(bit);
124         else
125                 val &= ~BIT(bit);
126         writel(val, smsm->shared_state + word);
127
128         /* XXX: send out interrupts */
129         for (i = 0; i < SMSM_MAX_STATES; i++) {
130                 ipc_state = &smsm->states[i];
131                 if (!ipc_state->ipc_regmap)
132                         continue;
133
134                 regmap_write(ipc_state->ipc_regmap, ipc_state->ipc_offset, BIT(ipc_state->ipc_bit));
135         }
136
137         dev_err(smsm->dev, "set %d %d\n", offset, value);
138
139         return 0;
140 }
141
142 static int smsm_gpio_get(struct gpio_chip *chip, unsigned offset)
143 {
144         struct qcom_smsm_state *state = to_smsm_state(chip);
145         struct qcom_smsm *smsm = to_smsm(state);
146         unsigned word;
147         unsigned bit;
148         u32 val;
149
150         offset += state->state_id * 32;
151
152         word = ALIGN(offset / 32, 4);
153         bit = offset % 32;
154
155         val = readl(smsm->shared_state + word);
156
157         return !!(val & BIT(bit));
158 }
159
160 static void smsm_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
161 {
162         smsm_gpio_direction_output(chip, offset, value);
163 }
164
165 static int smsm_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
166 {
167         return -EINVAL;
168 }
169
170 #ifdef CONFIG_DEBUG_FS
171 #include <linux/seq_file.h>
172
173 static void smsm_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
174 {
175         struct qcom_smsm_state *state = to_smsm_state(chip);
176         struct qcom_smsm *smsm = to_smsm(state);
177         unsigned i;
178         u32 val;
179
180         val = readl(smsm->shared_state + state->state_id * 4);
181
182         for (i = 0; i < 32; i++) {
183                 if (val & BIT(i))
184                         seq_puts(s, "1");
185                 else
186                         seq_puts(s, "0");
187
188                 if (i == 7 || i == 15 || i == 23)
189                         seq_puts(s, " ");
190         }
191         seq_puts(s, "\n");
192 }
193
194 #else
195 #define smsm_gpio_dbg_show NULL
196 #endif
197
198 static struct gpio_chip smsm_gpio_template = {
199         .direction_input = smsm_gpio_direction_input,
200         .direction_output = smsm_gpio_direction_output,
201         .get = smsm_gpio_get,
202         .set = smsm_gpio_set,
203         .to_irq = smsm_gpio_to_irq,
204         .dbg_show = smsm_gpio_dbg_show,
205         .owner = THIS_MODULE,
206 };
207
208 static int qcom_smsm_probe(struct platform_device *pdev)
209 {
210         struct qcom_smsm_state *state;
211         struct device_node *syscon_np;
212         struct device_node *node;
213         struct qcom_smsm *smsm;
214         char *key;
215         u32 sid;
216         int ret;
217
218         smsm = devm_kzalloc(&pdev->dev, sizeof(*smsm), GFP_KERNEL);
219         if (!smsm)
220                 return -ENOMEM;
221         smsm->dev = &pdev->dev;
222
223         ret = qcom_smem_alloc(-1, SMEM_SMSM_SHARED_STATE, 8 * sizeof(uint32_t));
224         if (ret < 0 && ret != -EEXIST) {
225                 dev_err(&pdev->dev, "unable to allocate shared state entry\n");
226                 return ret;
227         }
228
229         smsm->shared_state = qcom_smem_get(-1, SMEM_SMSM_SHARED_STATE,
230                             &smsm->shared_state_size);
231         if (IS_ERR(smsm->shared_state)) {
232                 dev_err(&pdev->dev, "Unable to acquire shared state entry\n");
233                 return PTR_ERR(smsm->shared_state);
234         }
235
236         dev_err(smsm->dev, "SMEM_SMSM_SHARED_STATE: %d, %zu\n", ret, smsm->shared_state_size);
237         print_hex_dump(KERN_DEBUG, "raw data: ", DUMP_PREFIX_OFFSET, 16, 1, smsm->shared_state, smsm->shared_state_size, true);
238
239         for_each_child_of_node(pdev->dev.of_node, node) {
240                 key = "reg";
241                 ret = of_property_read_u32(node, key, &sid);
242                 if (ret || sid >= SMSM_MAX_STATES) {
243                         dev_err(&pdev->dev, "smsm state missing %s\n", key);
244                         return -EINVAL;
245                 }
246                 state = &smsm->states[sid];
247                 state->state_id = sid;
248
249                 state->chip = smsm_gpio_template;
250                 state->chip.base = -1;
251                 state->chip.dev = &pdev->dev;
252                 state->chip.of_node = node;
253                 state->chip.label = node->name;
254                 state->chip.ngpio = 8 * sizeof(u32);
255                 ret = gpiochip_add(&state->chip);
256                 if (ret) {
257                         dev_err(&pdev->dev, "failed register gpiochip\n");
258                         // goto wooha;
259                 }
260
261                 /* The remaining properties are only for non-apps state */
262                 if (sid == SMSM_APPS_STATE)
263                         continue;
264
265                 state->irq = irq_of_parse_and_map(node, 0);
266                 if (state->irq < 0 && state->irq != -EINVAL) {
267                         dev_err(&pdev->dev, "failed to parse smsm interrupt\n");
268                         return -EINVAL;
269                 }
270
271                 syscon_np = of_parse_phandle(node, "qcom,ipc", 0);
272                 if (!syscon_np) {
273                         dev_err(&pdev->dev, "no qcom,ipc node\n");
274                         return -ENODEV;
275                 }
276
277                 state->ipc_regmap = syscon_node_to_regmap(syscon_np);
278                 if (IS_ERR(state->ipc_regmap))
279                         return PTR_ERR(state->ipc_regmap);
280
281                 key = "qcom,ipc";
282                 ret = of_property_read_u32_index(node, key, 1, &state->ipc_offset);
283                 if (ret < 0) {
284                         dev_err(&pdev->dev, "no offset in %s\n", key);
285                         return -EINVAL;
286                 }
287
288                 ret = of_property_read_u32_index(node, key, 2, &state->ipc_bit);
289                 if (ret < 0) {
290                         dev_err(&pdev->dev, "no bit in %s\n", key);
291                         return -EINVAL;
292                 }
293
294         }
295
296         return 0;
297 }
298
299 static const struct of_device_id qcom_smsm_of_match[] = {
300         { .compatible = "qcom,smsm" },
301         {}
302 };
303 MODULE_DEVICE_TABLE(of, qcom_smsm_of_match);
304
305 static struct platform_driver qcom_smsm_driver = {
306         .probe          = qcom_smsm_probe,
307         .driver  = {
308                 .name  = "qcom_smsm",
309                 .owner = THIS_MODULE,
310                 .of_match_table = qcom_smsm_of_match,
311         },
312 };
313
314 static int __init qcom_smsm_init(void)
315 {
316         return platform_driver_register(&qcom_smsm_driver);
317 }
318 arch_initcall(qcom_smsm_init);
319
320 static void __exit qcom_smsm_exit(void)
321 {
322         platform_driver_unregister(&qcom_smsm_driver);
323 }
324 module_exit(qcom_smsm_exit)
325
326 MODULE_DESCRIPTION("Qualcomm Shared Memory Signaling Mechanism");
327 MODULE_LICENSE("GPLv2");