mirror of
				https://github.com/smaeul/u-boot.git
				synced 2025-11-04 05:50:17 +00:00 
			
		
		
		
	MAXIM Semiconductor's PMIC, MAX77663 has 8 GPIO pins and 3 GPIO-like pins. It also supports interrupts from these pins. Add GPIO driver for these pins to control via GPIO APIs. Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
		
			
				
	
	
		
			179 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			179 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
// SPDX-License-Identifier: GPL-2.0+
 | 
						|
/*
 | 
						|
 *  Copyright(C) 2023 Svyatoslav Ryhel <clamor95@gmail.com>
 | 
						|
 */
 | 
						|
 | 
						|
#include <dm.h>
 | 
						|
#include <asm/gpio.h>
 | 
						|
#include <power/max77663.h>
 | 
						|
#include <power/pmic.h>
 | 
						|
 | 
						|
#define NUM_ENTRIES				11 /* 8 GPIOs + 3 KEYs  */
 | 
						|
#define NUM_GPIOS				8
 | 
						|
 | 
						|
#define MAX77663_CNFG1_GPIO			0x36
 | 
						|
#define GPIO_REG_ADDR(offset)			(MAX77663_CNFG1_GPIO + (offset))
 | 
						|
 | 
						|
#define MAX77663_CNFG_GPIO_DIR_MASK		BIT(1)
 | 
						|
#define MAX77663_CNFG_GPIO_DIR_INPUT		BIT(1)
 | 
						|
#define MAX77663_CNFG_GPIO_DIR_OUTPUT		0
 | 
						|
#define MAX77663_CNFG_GPIO_INPUT_VAL_MASK	BIT(2)
 | 
						|
#define MAX77663_CNFG_GPIO_OUTPUT_VAL_MASK	BIT(3)
 | 
						|
#define MAX77663_CNFG_GPIO_OUTPUT_VAL_HIGH	BIT(3)
 | 
						|
#define MAX77663_CNFG_GPIO_OUTPUT_VAL_LOW	0
 | 
						|
#define MAX77663_CNFG_IRQ			GENMASK(5, 4)
 | 
						|
 | 
						|
#define MAX77663_ONOFFSTAT_REG			0x15
 | 
						|
#define   EN0					BIT(2) /* KEY 2 */
 | 
						|
#define   ACOK					BIT(1) /* KEY 1 */
 | 
						|
#define   LID					BIT(0) /* KEY 0 */
 | 
						|
 | 
						|
static int max77663_gpio_direction_input(struct udevice *dev, unsigned int offset)
 | 
						|
{
 | 
						|
	int ret;
 | 
						|
 | 
						|
	if (offset >= NUM_GPIOS)
 | 
						|
		return 0;
 | 
						|
 | 
						|
	ret = pmic_clrsetbits(dev->parent, GPIO_REG_ADDR(offset),
 | 
						|
			      MAX77663_CNFG_GPIO_DIR_MASK,
 | 
						|
			      MAX77663_CNFG_GPIO_DIR_INPUT);
 | 
						|
	if (ret < 0)
 | 
						|
		log_debug("%s: CNFG_GPIOx dir update failed: %d\n", __func__, ret);
 | 
						|
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
static int max77663_gpio_direction_output(struct udevice *dev, unsigned int offset,
 | 
						|
					  int value)
 | 
						|
{
 | 
						|
	u8 val;
 | 
						|
	int ret;
 | 
						|
 | 
						|
	if (offset >= NUM_GPIOS)
 | 
						|
		return -EINVAL;
 | 
						|
 | 
						|
	val = (value) ? MAX77663_CNFG_GPIO_OUTPUT_VAL_HIGH :
 | 
						|
				MAX77663_CNFG_GPIO_OUTPUT_VAL_LOW;
 | 
						|
 | 
						|
	ret = pmic_clrsetbits(dev->parent, GPIO_REG_ADDR(offset),
 | 
						|
			      MAX77663_CNFG_GPIO_OUTPUT_VAL_MASK, val);
 | 
						|
	if (ret < 0) {
 | 
						|
		log_debug("%s: CNFG_GPIOx val update failed: %d\n", __func__, ret);
 | 
						|
		return ret;
 | 
						|
	}
 | 
						|
 | 
						|
	ret = pmic_clrsetbits(dev->parent, GPIO_REG_ADDR(offset),
 | 
						|
			      MAX77663_CNFG_GPIO_DIR_MASK,
 | 
						|
			      MAX77663_CNFG_GPIO_DIR_OUTPUT);
 | 
						|
	if (ret < 0)
 | 
						|
		log_debug("%s: CNFG_GPIOx dir update failed: %d\n", __func__, ret);
 | 
						|
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
static int max77663_gpio_get_value(struct udevice *dev, unsigned int offset)
 | 
						|
{
 | 
						|
	int ret;
 | 
						|
 | 
						|
	if (offset >= NUM_GPIOS) {
 | 
						|
		ret = pmic_reg_read(dev->parent, MAX77663_ONOFFSTAT_REG);
 | 
						|
		if (ret < 0) {
 | 
						|
			log_debug("%s: ONOFFSTAT_REG read failed: %d\n", __func__, ret);
 | 
						|
			return ret;
 | 
						|
		}
 | 
						|
 | 
						|
		return !!(ret & BIT(offset - NUM_GPIOS));
 | 
						|
	}
 | 
						|
 | 
						|
	ret = pmic_reg_read(dev->parent, GPIO_REG_ADDR(offset));
 | 
						|
	if (ret < 0) {
 | 
						|
		log_debug("%s: CNFG_GPIOx read failed: %d\n", __func__, ret);
 | 
						|
		return ret;
 | 
						|
	}
 | 
						|
 | 
						|
	if (ret & MAX77663_CNFG_GPIO_DIR_MASK)
 | 
						|
		return !!(ret & MAX77663_CNFG_GPIO_INPUT_VAL_MASK);
 | 
						|
	else
 | 
						|
		return !!(ret & MAX77663_CNFG_GPIO_OUTPUT_VAL_MASK);
 | 
						|
}
 | 
						|
 | 
						|
static int max77663_gpio_set_value(struct udevice *dev, unsigned int offset,
 | 
						|
				   int value)
 | 
						|
{
 | 
						|
	u8 val;
 | 
						|
	int ret;
 | 
						|
 | 
						|
	if (offset >= NUM_GPIOS)
 | 
						|
		return -EINVAL;
 | 
						|
 | 
						|
	val = (value) ? MAX77663_CNFG_GPIO_OUTPUT_VAL_HIGH :
 | 
						|
				MAX77663_CNFG_GPIO_OUTPUT_VAL_LOW;
 | 
						|
 | 
						|
	ret = pmic_clrsetbits(dev->parent, GPIO_REG_ADDR(offset),
 | 
						|
			      MAX77663_CNFG_GPIO_OUTPUT_VAL_MASK, val);
 | 
						|
	if (ret < 0)
 | 
						|
		log_debug("%s: CNFG_GPIO_OUT update failed: %d\n", __func__, ret);
 | 
						|
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
static int max77663_gpio_get_function(struct udevice *dev, unsigned int offset)
 | 
						|
{
 | 
						|
	int ret;
 | 
						|
 | 
						|
	if (offset >= NUM_GPIOS)
 | 
						|
		return GPIOF_INPUT;
 | 
						|
 | 
						|
	ret = pmic_reg_read(dev->parent, GPIO_REG_ADDR(offset));
 | 
						|
	if (ret < 0) {
 | 
						|
		log_debug("%s: CNFG_GPIOx read failed: %d\n", __func__, ret);
 | 
						|
		return ret;
 | 
						|
	}
 | 
						|
 | 
						|
	if (ret & MAX77663_CNFG_GPIO_DIR_MASK)
 | 
						|
		return GPIOF_INPUT;
 | 
						|
	else
 | 
						|
		return GPIOF_OUTPUT;
 | 
						|
}
 | 
						|
 | 
						|
static const struct dm_gpio_ops max77663_gpio_ops = {
 | 
						|
	.direction_input	= max77663_gpio_direction_input,
 | 
						|
	.direction_output	= max77663_gpio_direction_output,
 | 
						|
	.get_value		= max77663_gpio_get_value,
 | 
						|
	.set_value		= max77663_gpio_set_value,
 | 
						|
	.get_function		= max77663_gpio_get_function,
 | 
						|
};
 | 
						|
 | 
						|
static int max77663_gpio_probe(struct udevice *dev)
 | 
						|
{
 | 
						|
	struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
 | 
						|
	int i, ret;
 | 
						|
 | 
						|
	uc_priv->gpio_count = NUM_ENTRIES;
 | 
						|
	uc_priv->bank_name = "GPIO";
 | 
						|
 | 
						|
	/*
 | 
						|
	 * GPIO interrupts may be left ON after bootloader, hence let's
 | 
						|
	 * pre-initialize hardware to the expected state by disabling all
 | 
						|
	 * the interrupts.
 | 
						|
	 */
 | 
						|
	for (i = 0; i < NUM_GPIOS; i++) {
 | 
						|
		ret = pmic_clrsetbits(dev->parent, GPIO_REG_ADDR(i),
 | 
						|
				      MAX77663_CNFG_IRQ, 0);
 | 
						|
		if (ret < 0) {
 | 
						|
			log_debug("%s: failed to disable interrupt: %d\n", __func__, ret);
 | 
						|
			return ret;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
U_BOOT_DRIVER(max77663_gpio) = {
 | 
						|
	.name	= MAX77663_GPIO_DRIVER,
 | 
						|
	.id	= UCLASS_GPIO,
 | 
						|
	.probe	= max77663_gpio_probe,
 | 
						|
	.ops	= &max77663_gpio_ops,
 | 
						|
};
 |