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