xref: /linux/drivers/gpio/gpio-macsmc.c (revision 0227b49b50276657243e54f5609e65c4f0eaaf4d)
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 
71 struct macsmc_gpio {
72 	struct device *dev;
73 	struct apple_smc *smc;
74 	struct gpio_chip gc;
75 
76 	int first_index;
77 };
78 
macsmc_gpio_nr(smc_key key)79 static 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 
macsmc_gpio_key(unsigned int offset)90 static 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 
macsmc_gpio_find_first_gpio_index(struct macsmc_gpio * smcgp)95 static 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 
macsmc_gpio_get_direction(struct gpio_chip * gc,unsigned int offset)142 static 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 
macsmc_gpio_get(struct gpio_chip * gc,unsigned int offset)162 static 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 
macsmc_gpio_set(struct gpio_chip * gc,unsigned int offset,int value)185 static 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 
macsmc_gpio_init_valid_mask(struct gpio_chip * gc,unsigned long * valid_mask,unsigned int ngpios)200 static 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 
macsmc_gpio_probe(struct platform_device * pdev)234 static 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 
275 static const struct of_device_id macsmc_gpio_of_table[] = {
276 	{ .compatible = "apple,smc-gpio", },
277 	{}
278 };
279 MODULE_DEVICE_TABLE(of, macsmc_gpio_of_table);
280 
281 static 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 };
288 module_platform_driver(macsmc_gpio_driver);
289 
290 MODULE_AUTHOR("Hector Martin <marcan@marcan.st>");
291 MODULE_LICENSE("Dual MIT/GPL");
292 MODULE_DESCRIPTION("Apple SMC GPIO driver");
293