Linux Audio

Check our new training course

Embedded Linux Audio

Check our new training course
with Creative Commons CC-BY-SA
lecture materials

Bootlin logo

Elixir Cross Referencer

Loading...
/*
 * Copyright (c) 2020 ITE Corporation. All Rights Reserved.
 *
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @brief PINMUX driver for the IT8xxx2
 */

#include <errno.h>
#include <device.h>
#include <drivers/pinmux.h>
#include <soc.h>
#include <dt-bindings/pinctrl/it8xxx2-pinctrl.h>

#define DT_DRV_COMPAT ite_it8xxx2_pinmux

#include <logging/log.h>
LOG_MODULE_REGISTER(pinmux_ite_it8xxx2, LOG_LEVEL_ERR);

struct pinmux_it8xxx2_config {
	/* gpio port control register (byte mapping to pin) */
	uintptr_t reg_gpcr;
	/* function 3 general control register */
	uintptr_t func3_gcr[8];
	/* function 4 general control register */
	uintptr_t func4_gcr[8];
	/* function 3 enable mask */
	uint8_t func3_en_mask[8];
	/* function 4 enable mask */
	uint8_t func4_en_mask[8];
};

#define DEV_CFG(dev)					\
	((const struct pinmux_it8xxx2_config * const)	\
	 (dev)->config)

static int pinmux_it8xxx2_set(const struct device *dev,
			      uint32_t pin, uint32_t func)
{
	const struct pinmux_it8xxx2_config *pinmux_config = DEV_CFG(dev);

	volatile uint8_t *reg_gpcr =
		(uint8_t *)(pinmux_config->reg_gpcr + pin);
	volatile uint8_t *reg_func3_gcr =
		(uint8_t *)(pinmux_config->func3_gcr[pin]);
	volatile uint8_t *reg_func4_gcr =
		(uint8_t *)(pinmux_config->func4_gcr[pin]);

	if (pin >= IT8XXX2_PINMUX_PINS) {
		return -EINVAL;
	}

	/* Common settings for alternate function. */
	*reg_gpcr &= ~(GPCR_PORT_PIN_MODE_INPUT |
		GPCR_PORT_PIN_MODE_OUTPUT);

	switch (func) {
	case IT8XXX2_PINMUX_FUNC_1:
		/* Func1: Alternate function has been set above. */
		break;
	case IT8XXX2_PINMUX_FUNC_2:
		/* Func2: WUI function: turn the pin into an input */
		*reg_gpcr |= GPCR_PORT_PIN_MODE_INPUT;
		break;
	case IT8XXX2_PINMUX_FUNC_3:
		/*
		 * Func3: In addition to the alternate setting above,
		 *        Func3 also need to set the general control.
		 */
		*reg_func3_gcr |= pinmux_config->func3_en_mask[pin];
		break;
	case IT8XXX2_PINMUX_FUNC_4:
		/*
		 * Func4: In addition to the alternate setting above,
		 *        Func4 also need to set the general control.
		 */
		*reg_func4_gcr |= pinmux_config->func4_en_mask[pin];
		break;
	default:
		LOG_ERR("This function is not supported");
		return -EINVAL;
	}

	return 0;
}

static int pinmux_it8xxx2_get(const struct device *dev,
			      uint32_t pin, uint32_t *func)
{
	const struct pinmux_it8xxx2_config *pinmux_config = DEV_CFG(dev);

	volatile uint8_t *reg_gpcr =
		(uint8_t *)(pinmux_config->reg_gpcr + pin);

	if (pin >= IT8XXX2_PINMUX_PINS || func == NULL) {
		return -EINVAL;
	}

	*func = (*reg_gpcr & (GPCR_PORT_PIN_MODE_INPUT |
		GPCR_PORT_PIN_MODE_OUTPUT)) == GPCR_PORT_PIN_MODE_INPUT ?
		IT8XXX2_PINMUX_FUNC_2 : IT8XXX2_PINMUX_FUNC_1;

	/* TODO: IT8XXX2_PINMUX_FUNC_3 & IT8XXX2_PINMUX_FUNC_4 */

	return 0;
}

static int pinmux_it8xxx2_pullup(const struct device *dev,
				 uint32_t pin, uint8_t func)
{
	const struct pinmux_it8xxx2_config *pinmux_config = DEV_CFG(dev);

	volatile uint8_t *reg_gpcr =
		(uint8_t *)(pinmux_config->reg_gpcr + pin);

	if (func == PINMUX_PULLUP_ENABLE) {
		*reg_gpcr = (*reg_gpcr | GPCR_PORT_PIN_MODE_PULLUP) &
			~GPCR_PORT_PIN_MODE_PULLDOWN;
	} else if (func == PINMUX_PULLUP_DISABLE) {
		*reg_gpcr &= ~(GPCR_PORT_PIN_MODE_PULLUP |
			GPCR_PORT_PIN_MODE_PULLDOWN);
	} else {
		return -EINVAL;
	}

	return 0;
}

static int pinmux_it8xxx2_input(const struct device *dev,
				uint32_t pin, uint8_t func)
{
	const struct pinmux_it8xxx2_config *pinmux_config = DEV_CFG(dev);

	volatile uint8_t *reg_gpcr =
		(uint8_t *)(pinmux_config->reg_gpcr + pin);

	*reg_gpcr &= ~(GPCR_PORT_PIN_MODE_INPUT |
		GPCR_PORT_PIN_MODE_OUTPUT);

	if (func == PINMUX_INPUT_ENABLED) {
		*reg_gpcr |= GPCR_PORT_PIN_MODE_INPUT;
	} else if (func == PINMUX_OUTPUT_ENABLED) {
		*reg_gpcr |= GPCR_PORT_PIN_MODE_OUTPUT;
	} else {
		return -EINVAL;
	}

	return 0;
}

static int pinmux_it8xxx2_init(const struct device *dev)
{
	ARG_UNUSED(dev);

	/*
	 * The default value of LPCRSTEN is bit2:1 = 10b(GPD2) in GCR.
	 * If LPC reset is enabled on GPB7, we have to clear bit2:1
	 * to 00b.
	 */
	IT8XXX2_GPIO_GCR &= ~(BIT(1) | BIT(2));

	/*
	 * TODO: If SMBUS3 swaps from H group to F group, we have to
	 * set SMB3PSEL = 1 in PMER3 register.
	 */

	/*
	 * TODO: If UART2 swaps from bit2:1 to bit6:5 in H group, we
	 * have to set UART1PSEL = 1 in UART1PMR register.
	 */

	return 0;
}

static const struct pinmux_driver_api pinmux_it8xxx2_driver_api = {
	.set = pinmux_it8xxx2_set,
	.get = pinmux_it8xxx2_get,
	.pullup = pinmux_it8xxx2_pullup,
	.input = pinmux_it8xxx2_input,
};

#define PINMUX_ITE_INIT(inst)							\
	static const struct pinmux_it8xxx2_config pinmux_it8xxx2_cfg_##inst = {	\
		.reg_gpcr = DT_INST_REG_ADDR(inst),				\
		.func3_gcr = DT_INST_PROP(inst, func3_gcr),			\
		.func3_en_mask = DT_INST_PROP(inst, func3_en_mask),		\
		.func4_gcr = DT_INST_PROP(inst, func4_gcr),			\
		.func4_en_mask = DT_INST_PROP(inst, func4_en_mask),		\
	};									\
										\
	DEVICE_DT_INST_DEFINE(inst,						\
			    &pinmux_it8xxx2_init,				\
			    NULL, NULL, &pinmux_it8xxx2_cfg_##inst,		\
			    PRE_KERNEL_1,					\
			    CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,		\
			    &pinmux_it8xxx2_driver_api);

DT_INST_FOREACH_STATUS_OKAY(PINMUX_ITE_INIT)