Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: GPL-2.0-only OR MIT
2/*
3 * Apple SMC GPIO driver
4 * Copyright The Asahi Linux Contributors
5 *
6 * This driver implements basic SMC PMU GPIO support that can read inputs
7 * and write outputs. Mode changes and IRQ config are not yet implemented.
8 */
9
10#include <linux/bitmap.h>
11#include <linux/device.h>
12#include <linux/gpio/driver.h>
13#include <linux/hex.h>
14#include <linux/mfd/core.h>
15#include <linux/mfd/macsmc.h>
16
17#define MAX_GPIO 64
18
19/*
20 * Commands 0-6 are, presumably, the intended API.
21 * Command 0xff lets you get/set the pin configuration in detail directly,
22 * but the bit meanings seem not to be stable between devices/PMU hardware
23 * versions.
24 *
25 * We're going to try to make do with the low commands for now.
26 * We don't implement pin mode changes at this time.
27 */
28
29#define CMD_ACTION (0 << 24)
30#define CMD_OUTPUT (1 << 24)
31#define CMD_INPUT (2 << 24)
32#define CMD_PINMODE (3 << 24)
33#define CMD_IRQ_ENABLE (4 << 24)
34#define CMD_IRQ_ACK (5 << 24)
35#define CMD_IRQ_MODE (6 << 24)
36#define CMD_CONFIG (0xff << 24)
37
38#define MODE_INPUT 0
39#define MODE_OUTPUT 1
40#define MODE_VALUE_0 0
41#define MODE_VALUE_1 2
42
43#define IRQ_MODE_HIGH 0
44#define IRQ_MODE_LOW 1
45#define IRQ_MODE_RISING 2
46#define IRQ_MODE_FALLING 3
47#define IRQ_MODE_BOTH 4
48
49#define CONFIG_MASK GENMASK(23, 16)
50#define CONFIG_VAL GENMASK(7, 0)
51
52#define CONFIG_OUTMODE GENMASK(7, 6)
53#define CONFIG_IRQMODE GENMASK(5, 3)
54#define CONFIG_PULLDOWN BIT(2)
55#define CONFIG_PULLUP BIT(1)
56#define CONFIG_OUTVAL BIT(0)
57
58/*
59 * Output modes seem to differ depending on the PMU in use... ?
60 * j274 / M1 (Sera PMU):
61 * 0 = input
62 * 1 = output
63 * 2 = open drain
64 * 3 = disable
65 * j314 / M1Pro (Maverick PMU):
66 * 0 = input
67 * 1 = open drain
68 * 2 = output
69 * 3 = ?
70 */
71
72struct macsmc_gpio {
73 struct device *dev;
74 struct apple_smc *smc;
75 struct gpio_chip gc;
76
77 int first_index;
78};
79
80static int macsmc_gpio_nr(smc_key key)
81{
82 int low = hex_to_bin(key & 0xff);
83 int high = hex_to_bin((key >> 8) & 0xff);
84
85 if (low < 0 || high < 0)
86 return -1;
87
88 return low | (high << 4);
89}
90
91static int macsmc_gpio_key(unsigned int offset)
92{
93 return _SMC_KEY("gP\0\0") | hex_asc_hi(offset) << 8 | hex_asc_lo(offset);
94}
95
96static int macsmc_gpio_find_first_gpio_index(struct macsmc_gpio *smcgp)
97{
98 struct apple_smc *smc = smcgp->smc;
99 smc_key key = macsmc_gpio_key(0);
100 smc_key first_key, last_key;
101 int start, count, ret;
102
103 /* Return early if the key is out of bounds */
104 ret = apple_smc_get_key_by_index(smc, 0, &first_key);
105 if (ret)
106 return ret;
107 if (key <= first_key)
108 return -ENODEV;
109
110 ret = apple_smc_get_key_by_index(smc, smc->key_count - 1, &last_key);
111 if (ret)
112 return ret;
113 if (key > last_key)
114 return -ENODEV;
115
116 /* Binary search to find index of first SMC key bigger or equal to key */
117 start = 0;
118 count = smc->key_count;
119 while (count > 1) {
120 smc_key pkey;
121 int pivot = start + ((count - 1) >> 1);
122
123 ret = apple_smc_get_key_by_index(smc, pivot, &pkey);
124 if (ret < 0)
125 return ret;
126
127 if (pkey == key)
128 return pivot;
129
130 pivot++;
131
132 if (pkey < key) {
133 count -= pivot - start;
134 start = pivot;
135 } else {
136 count = pivot - start;
137 }
138 }
139
140 return start;
141}
142
143static int macsmc_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
144{
145 struct macsmc_gpio *smcgp = gpiochip_get_data(gc);
146 smc_key key = macsmc_gpio_key(offset);
147 u32 val;
148 int ret;
149
150 /* First try reading the explicit pin mode register */
151 ret = apple_smc_rw_u32(smcgp->smc, key, CMD_PINMODE, &val);
152 if (!ret)
153 return (val & MODE_OUTPUT) ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN;
154
155 /*
156 * Less common IRQ configs cause CMD_PINMODE to fail, and so does open drain mode.
157 * Fall back to reading IRQ mode, which will only succeed for inputs.
158 */
159 ret = apple_smc_rw_u32(smcgp->smc, key, CMD_IRQ_MODE, &val);
160 return ret ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN;
161}
162
163static int macsmc_gpio_get(struct gpio_chip *gc, unsigned int offset)
164{
165 struct macsmc_gpio *smcgp = gpiochip_get_data(gc);
166 smc_key key = macsmc_gpio_key(offset);
167 u32 cmd, val;
168 int ret;
169
170 ret = macsmc_gpio_get_direction(gc, offset);
171 if (ret < 0)
172 return ret;
173
174 if (ret == GPIO_LINE_DIRECTION_OUT)
175 cmd = CMD_OUTPUT;
176 else
177 cmd = CMD_INPUT;
178
179 ret = apple_smc_rw_u32(smcgp->smc, key, cmd, &val);
180 if (ret < 0)
181 return ret;
182
183 return val ? 1 : 0;
184}
185
186static int macsmc_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)
187{
188 struct macsmc_gpio *smcgp = gpiochip_get_data(gc);
189 smc_key key = macsmc_gpio_key(offset);
190 int ret;
191
192 value |= CMD_OUTPUT;
193 ret = apple_smc_write_u32(smcgp->smc, key, CMD_OUTPUT | value);
194 if (ret < 0)
195 dev_err(smcgp->dev, "GPIO set failed %p4ch = 0x%x\n",
196 &key, value);
197
198 return ret;
199}
200
201static int macsmc_gpio_init_valid_mask(struct gpio_chip *gc,
202 unsigned long *valid_mask, unsigned int ngpios)
203{
204 struct macsmc_gpio *smcgp = gpiochip_get_data(gc);
205 int count;
206 int i;
207
208 count = min(smcgp->smc->key_count, MAX_GPIO);
209
210 bitmap_zero(valid_mask, ngpios);
211
212 for (i = 0; i < count; i++) {
213 int ret, gpio_nr;
214 smc_key key;
215
216 ret = apple_smc_get_key_by_index(smcgp->smc, smcgp->first_index + i, &key);
217 if (ret < 0)
218 return ret;
219
220 if (key > SMC_KEY(gPff))
221 break;
222
223 gpio_nr = macsmc_gpio_nr(key);
224 if (gpio_nr < 0 || gpio_nr > MAX_GPIO) {
225 dev_err(smcgp->dev, "Bad GPIO key %p4ch\n", &key);
226 continue;
227 }
228
229 set_bit(gpio_nr, valid_mask);
230 }
231
232 return 0;
233}
234
235static int macsmc_gpio_probe(struct platform_device *pdev)
236{
237 struct macsmc_gpio *smcgp;
238 struct apple_smc *smc = dev_get_drvdata(pdev->dev.parent);
239 smc_key key;
240 int ret;
241
242 smcgp = devm_kzalloc(&pdev->dev, sizeof(*smcgp), GFP_KERNEL);
243 if (!smcgp)
244 return -ENOMEM;
245
246 smcgp->dev = &pdev->dev;
247 smcgp->smc = smc;
248
249 smcgp->first_index = macsmc_gpio_find_first_gpio_index(smcgp);
250 if (smcgp->first_index < 0)
251 return smcgp->first_index;
252
253 ret = apple_smc_get_key_by_index(smc, smcgp->first_index, &key);
254 if (ret < 0)
255 return ret;
256
257 if (key > macsmc_gpio_key(MAX_GPIO - 1))
258 return -ENODEV;
259
260 dev_info(smcgp->dev, "First GPIO key: %p4ch\n", &key);
261
262 smcgp->gc.label = "macsmc-pmu-gpio";
263 smcgp->gc.owner = THIS_MODULE;
264 smcgp->gc.get = macsmc_gpio_get;
265 smcgp->gc.set = macsmc_gpio_set;
266 smcgp->gc.get_direction = macsmc_gpio_get_direction;
267 smcgp->gc.init_valid_mask = macsmc_gpio_init_valid_mask;
268 smcgp->gc.can_sleep = true;
269 smcgp->gc.ngpio = MAX_GPIO;
270 smcgp->gc.base = -1;
271 smcgp->gc.parent = &pdev->dev;
272
273 return devm_gpiochip_add_data(&pdev->dev, &smcgp->gc, smcgp);
274}
275
276static const struct of_device_id macsmc_gpio_of_table[] = {
277 { .compatible = "apple,smc-gpio", },
278 {}
279};
280MODULE_DEVICE_TABLE(of, macsmc_gpio_of_table);
281
282static struct platform_driver macsmc_gpio_driver = {
283 .driver = {
284 .name = "macsmc-gpio",
285 .of_match_table = macsmc_gpio_of_table,
286 },
287 .probe = macsmc_gpio_probe,
288};
289module_platform_driver(macsmc_gpio_driver);
290
291MODULE_AUTHOR("Hector Martin <marcan@marcan.st>");
292MODULE_LICENSE("Dual MIT/GPL");
293MODULE_DESCRIPTION("Apple SMC GPIO driver");