File Index
Files and directories
Sign in
FILE

gpio-macsmc.c

File preview with syntax highlighting.

linux-6.19.14/drivers/gpio/gpio-macsmc.c C
  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");
~/ Read-only file preview.