drivers/gpio/gpio-macsmc.c

Source file repositories/reference/linux-study-clean/drivers/gpio/gpio-macsmc.c

File Facts

System
Linux kernel
Corpus path
drivers/gpio/gpio-macsmc.c
Extension
.c
Size
6941 bytes
Lines
294
Domain
Driver Families
Bucket
drivers/gpio
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

struct macsmc_gpio {
	struct device *dev;
	struct apple_smc *smc;
	struct gpio_chip gc;

	int first_index;
};

static int macsmc_gpio_nr(smc_key key)
{
	int low = hex_to_bin(key & 0xff);
	int high = hex_to_bin((key >> 8) & 0xff);

	if (low < 0 || high < 0)
		return -1;

	return low | (high << 4);
}

static int macsmc_gpio_key(unsigned int offset)
{
	return _SMC_KEY("gP\0\0") | hex_asc_hi(offset) << 8 | hex_asc_lo(offset);
}

static int macsmc_gpio_find_first_gpio_index(struct macsmc_gpio *smcgp)
{
	struct apple_smc *smc = smcgp->smc;
	smc_key key = macsmc_gpio_key(0);
	smc_key first_key, last_key;
	int start, count, ret;

	/* Return early if the key is out of bounds */
	ret = apple_smc_get_key_by_index(smc, 0, &first_key);
	if (ret)
		return ret;
	if (key <= first_key)
		return -ENODEV;

	ret = apple_smc_get_key_by_index(smc, smc->key_count - 1, &last_key);
	if (ret)
		return ret;
	if (key > last_key)
		return -ENODEV;

	/* Binary search to find index of first SMC key bigger or equal to key */
	start = 0;
	count = smc->key_count;
	while (count > 1) {
		smc_key pkey;
		int pivot = start + ((count - 1) >> 1);

		ret = apple_smc_get_key_by_index(smc, pivot, &pkey);
		if (ret < 0)
			return ret;

		if (pkey == key)
			return pivot;

		pivot++;

		if (pkey < key) {
			count -= pivot - start;
			start = pivot;
		} else {
			count = pivot - start;
		}
	}

	return start;
}

static int macsmc_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
{
	struct macsmc_gpio *smcgp = gpiochip_get_data(gc);
	smc_key key = macsmc_gpio_key(offset);
	u32 val;
	int ret;

	/* First try reading the explicit pin mode register */
	ret = apple_smc_rw_u32(smcgp->smc, key, CMD_PINMODE, &val);
	if (!ret)
		return (val & MODE_OUTPUT) ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN;

	/*
	 * Less common IRQ configs cause CMD_PINMODE to fail, and so does open drain mode.
	 * Fall back to reading IRQ mode, which will only succeed for inputs.
	 */
	ret = apple_smc_rw_u32(smcgp->smc, key, CMD_IRQ_MODE, &val);
	return ret ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN;
}

Annotation

Implementation Notes