u-boot/drivers/gpio/max77663_gpio.c
Svyatoslav Ryhel 6b24c372c1 drivers: gpio: implement MAX77663 GPIO cell
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>
2023-12-19 20:53:53 +02:00

178 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,
};