From nobody Tue Feb 10 08:01:19 2026 Received: from mail-ej1-f42.google.com (mail-ej1-f42.google.com [209.85.218.42]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A204229B21D for ; Tue, 29 Apr 2025 08:22:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.218.42 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1745914935; cv=none; b=kQU8HgXpzKm92DwVQvuS58l3if4OI0m3fe3RkwsIHNMkitwKtkWI84uWI5RH6TUoXeusNOTXpjixnuo/x5XGqu8SE8aKhcR/0/gMiVSSARxciVbGY+67rZx9IYmhiL6cf94TIJu6RXfLZFy27k0gyEwFp0uzQ/b1vqr5qrOiYYA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1745914935; c=relaxed/simple; bh=fzKstZUVeQBSYl0cS0pI2cvSc+v8nYyTBa1Wr1QDJPU=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Uzo7uXQqC2JQvgp6V5/I8ogN4pBup31vT27KdscNA1ZANS5T0sdTDKMsXrrZEUpDRrD3XyP61PWhZvDAjVvkMxOrK2G7H0BmE46encboRXkWwim1SSXgo8YYEsqb6IpQkC83n21B9A4fuJUyK6RsrYGNfjgt7msSJtbi/csVuLI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linaro.org; spf=pass smtp.mailfrom=linaro.org; dkim=pass (2048-bit key) header.d=linaro.org header.i=@linaro.org header.b=yN+9lhIx; arc=none smtp.client-ip=209.85.218.42 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linaro.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linaro.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=linaro.org header.i=@linaro.org header.b="yN+9lhIx" Received: by mail-ej1-f42.google.com with SMTP id a640c23a62f3a-ace333d5f7bso927508166b.3 for ; Tue, 29 Apr 2025 01:22:11 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1745914930; x=1746519730; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=mygQ5tEgtTV0ftu+MUNaQUht9cpvP+4gLNPLiSnhTGk=; b=yN+9lhIxmLUNQQ6VTb7P2pYYJVQoRum3/Vzpndv5HyyiyW4o4vDjAgilaAe3J1i34B HylJHJ8HldSrZ9iTjxJVjyI4opF+eqmuQ9G66V7WvfjFojflMtBwkCCCe9KIDuvVFhaj AwxmZMJaU/2R5Za6M9efcd5PFcYh8f8TVLLp7eXPR97Z+1BFSuqUXdBcmrJMWeLoOhSi kVahVP7iYP7edpJTi41JFS5q+4aqUH4AC0+LX7+vOohx/dES7APMIpvFxTTghCOaULiq P03kn4ZeeT+z0s9RpeDVlhwfOzIhmpSw5Q9M8Oz7GSbrfDI2lTR7OxLQjoznfrXceGEQ fS3Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1745914930; x=1746519730; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=mygQ5tEgtTV0ftu+MUNaQUht9cpvP+4gLNPLiSnhTGk=; b=k0ypKRfetMnJ8OBfs83bT+V8oK2CrDDB00xSDZrqj560FulltAnz20hZdGWq9Mrktq B4+XVsvk/CZB1dcUjCQ9rhsk/kbcMEiDJdYq/Beuqy1TOYnwpgeVgKtyszRQg5ZwbvFE KwmyMSM2GvSU64K3OaPrGjwaORVZp2rAWJUhlHfLKK4dwzd7+cMesvLyGVZx/YT/Zmin 3SjgThraNQiPg86HPZF/6aqIH/5tTItgaPsKjyaAs41COdN2ksXqN0rESrS9hPWzMRvj NoeDIETeyczybx6bKPfO3RMH28SXAHCnG+rZyksbIRBvRLEQFw0vpj81t9j1RmEpiBzY u4ww== X-Forwarded-Encrypted: i=1; AJvYcCUGxLxTkx0t69jMmBQZhsXvVJvYCWAOVFgsFh5K2fPOIpcb8a+E/Oo4JPKjh6024JKVUVad1VmL75M7HhM=@vger.kernel.org X-Gm-Message-State: AOJu0Yzu/Di+bsXD0G5bZ0IM+B/W+o/LKk0bOuX6710TRSZFcKmMztdG h+MBV34dKtt/5re4pvXdRvWYcDLRakZEQPRCJZ43WwfN8RPhrs8AqmcuQkSLqqE= X-Gm-Gg: ASbGnctBoZOICzDmI49Vkq3hCIH6EISEHLHXRpKSg4jvJH3ViSx4eIfuPU4DdRIEqFB g+sDtO3Ot2tDx41/x6ISUCvQGEqAUHXx3rlAzqXHIp7kqgXF4q3JTV4PxFj7j+TeKOayEfEBaJm 8wZYhfvBiXCoiQrKgSZsQumRJNedmvYcVzv34gDrqnbES62JwYWrclXN1cJucJ02y5D+dhLBrto O0xfa6z67Jj3x00znZ0vGG4yz1fxqOWHliCE2QW/J6aYbg9ouF4Mj2Kd9+W5RB2oEw/zpFPw4C8 Tnx1GPa94cvKcsXKR5HSMLZKukAzwkF2Jm4PTsJ/qyeuDCSEBjJWdWdo9xfvvJXfeCQ+jj9kOgp pWk8S9V2XLdzj4GSzpiYqbe1O X-Google-Smtp-Source: AGHT+IGJBIyjyssjwSYc5tWtf3900ENeYx9rADm33OqV4nrIeVXZ4KdXhVTJ7NWmlVnzTjqPm/LxNw== X-Received: by 2002:a17:907:2d90:b0:ac3:5c8e:d3f5 with SMTP id a640c23a62f3a-ace8493ae33mr1140096066b.27.1745914929932; Tue, 29 Apr 2025 01:22:09 -0700 (PDT) Received: from puffmais.c.googlers.com (8.239.204.35.bc.googleusercontent.com. [35.204.239.8]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-ace6e41bbf4sm737905466b.36.2025.04.29.01.22.09 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 29 Apr 2025 01:22:09 -0700 (PDT) From: =?utf-8?q?Andr=C3=A9_Draszik?= Date: Tue, 29 Apr 2025 09:21:41 +0100 Subject: [PATCH v8 5/6] gpio: max77759: add Maxim MAX77759 gpio driver Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20250429-max77759-mfd-v8-5-72d72dc79a1f@linaro.org> References: <20250429-max77759-mfd-v8-0-72d72dc79a1f@linaro.org> In-Reply-To: <20250429-max77759-mfd-v8-0-72d72dc79a1f@linaro.org> To: Lee Jones , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Linus Walleij , Bartosz Golaszewski , Srinivas Kandagatla , Kees Cook , "Gustavo A. R. Silva" Cc: Peter Griffin , Tudor Ambarus , Will McVicker , kernel-team@android.com, linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, linux-gpio@vger.kernel.org, linux-hardening@vger.kernel.org, =?utf-8?q?Andr=C3=A9_Draszik?= X-Mailer: b4 0.14.2 The Maxim MAX77759 is a companion PMIC for USB Type-C applications and includes Battery Charger, Fuel Gauge, temperature sensors, USB Type-C Port Controller (TCPC), NVMEM, and a GPIO expander. This driver supports the GPIO functions using the platform device registered by the core MFD driver. Signed-off-by: Andr=C3=A9 Draszik Acked-by: Bartosz Golaszewski Reviewed-by: Linus Walleij --- v8: * switch to gpio_chip::set_rv() (Bartosz) * replace MODULE_ALIAS() with .id_table (Krzysztof) * drop previous tags v5: * follow API updates of max77759 core driver v3: * drop duplicate init of 'handled' variable in irq handler * use boolean with IRQ_RETVAL() (Linus) * drop 'virq' variable inside irq handler to avoid confusion (Linus) * drop assignment of struct gpio_chip::owner (Linus) v2: * fix max77759_gpio_direction_from_control() * add missing error handling of devm_mutex_init() (Christophe) * align sentinel in max77759_gpio_of_id[] with other max77759 drivers (Christophe) --- MAINTAINERS | 1 + drivers/gpio/Kconfig | 13 ++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-max77759.c | 530 +++++++++++++++++++++++++++++++++++++++= ++++ 4 files changed, 545 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 1259d09e700a21215e8ae2facef858df5304346e..0db5e1fe64930e85265913e6a7d= d2669c645cf42 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14668,6 +14668,7 @@ M: Andr=C3=A9 Draszik L: linux-kernel@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/*/maxim,max77759*.yaml +F: drivers/gpio/gpio-max77759.c F: drivers/mfd/max77759.c F: include/linux/mfd/max77759.h =20 diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 9ae806f45e19c1494d156b7f04b1882be68d3e3f..bbc71cdde9ed66b2fe69dcbc750= 8d51690d2cfa4 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1483,6 +1483,19 @@ config GPIO_MAX77650 GPIO driver for MAX77650/77651 PMIC from Maxim Semiconductor. These chips have a single pin that can be configured as GPIO. =20 +config GPIO_MAX77759 + tristate "Maxim Integrated MAX77759 GPIO support" + depends on MFD_MAX77759 + default MFD_MAX77759 + select GPIOLIB_IRQCHIP + help + GPIO driver for MAX77759 PMIC from Maxim Integrated. + There are two GPIOs available on these chips in total, both of + which can also generate interrupts. + + This driver can also be built as a module. If so, the module will be + called gpio-max77759. + config GPIO_PALMAS bool "TI PALMAS series PMICs GPIO" depends on MFD_PALMAS diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 9aabbb9cb4c61ea57833adf2edb265c204b42cdf..1abae4477ed76b88aff08e83f6d= 41e58d0b71ff5 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -106,6 +106,7 @@ obj-$(CONFIG_GPIO_MAX730X) +=3D gpio-max730x.o obj-$(CONFIG_GPIO_MAX732X) +=3D gpio-max732x.o obj-$(CONFIG_GPIO_MAX77620) +=3D gpio-max77620.o obj-$(CONFIG_GPIO_MAX77650) +=3D gpio-max77650.o +obj-$(CONFIG_GPIO_MAX77759) +=3D gpio-max77759.o obj-$(CONFIG_GPIO_MB86S7X) +=3D gpio-mb86s7x.o obj-$(CONFIG_GPIO_MC33880) +=3D gpio-mc33880.o obj-$(CONFIG_GPIO_MENZ127) +=3D gpio-menz127.o diff --git a/drivers/gpio/gpio-max77759.c b/drivers/gpio/gpio-max77759.c new file mode 100644 index 0000000000000000000000000000000000000000..7fe8e6f697d044ebf9c620f41e5= 9f150a6ae086a --- /dev/null +++ b/drivers/gpio/gpio-max77759.c @@ -0,0 +1,530 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright 2020 Google Inc +// Copyright 2025 Linaro Ltd. +// +// GPIO driver for Maxim MAX77759 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX77759_N_GPIOS ARRAY_SIZE(max77759_gpio_line_names) +static const char * const max77759_gpio_line_names[] =3D { "GPIO5", "GPIO6= " }; + +struct max77759_gpio_chip { + struct regmap *map; + struct max77759 *max77759; + struct gpio_chip gc; + struct mutex maxq_lock; /* protect MaxQ r/m/w operations */ + + struct mutex irq_lock; /* protect irq bus */ + int irq_mask; + int irq_mask_changed; + int irq_trig; + int irq_trig_changed; +}; + +#define MAX77759_GPIOx_TRIGGER(offs, val) (((val) & 1) << (offs)) +#define MAX77759_GPIOx_TRIGGER_MASK(offs) MAX77759_GPIOx_TRIGGER(offs, ~0) +enum max77759_trigger_gpio_type { + MAX77759_GPIO_TRIGGER_RISING =3D 0, + MAX77759_GPIO_TRIGGER_FALLING =3D 1 +}; + +#define MAX77759_GPIOx_DIR(offs, dir) (((dir) & 1) << (2 + (3 * (offs)))) +#define MAX77759_GPIOx_DIR_MASK(offs) MAX77759_GPIOx_DIR(offs, ~0) +enum max77759_control_gpio_dir { + MAX77759_GPIO_DIR_IN =3D 0, + MAX77759_GPIO_DIR_OUT =3D 1 +}; + +#define MAX77759_GPIOx_OUTVAL(offs, val) (((val) & 1) << (3 + (3 * (offs))= )) +#define MAX77759_GPIOx_OUTVAL_MASK(offs) MAX77759_GPIOx_OUTVAL(offs, ~0) + +#define MAX77759_GPIOx_INVAL_MASK(offs) (BIT(4) << (3 * (offs))) + +static int max77759_gpio_maxq_gpio_trigger_read(struct max77759_gpio_chip = *chip) +{ + DEFINE_FLEX(struct max77759_maxq_command, cmd, cmd, length, 1); + DEFINE_FLEX(struct max77759_maxq_response, rsp, rsp, length, 2); + int ret; + + cmd->cmd[0] =3D MAX77759_MAXQ_OPCODE_GPIO_TRIGGER_READ; + + ret =3D max77759_maxq_command(chip->max77759, cmd, rsp); + if (ret < 0) + return ret; + + return rsp->rsp[1]; +} + +static int max77759_gpio_maxq_gpio_trigger_write(struct max77759_gpio_chip= *chip, + u8 trigger) +{ + DEFINE_FLEX(struct max77759_maxq_command, cmd, cmd, length, 2); + + cmd->cmd[0] =3D MAX77759_MAXQ_OPCODE_GPIO_TRIGGER_WRITE; + cmd->cmd[1] =3D trigger; + + return max77759_maxq_command(chip->max77759, cmd, NULL); +} + +static int max77759_gpio_maxq_gpio_control_read(struct max77759_gpio_chip = *chip) +{ + DEFINE_FLEX(struct max77759_maxq_command, cmd, cmd, length, 1); + DEFINE_FLEX(struct max77759_maxq_response, rsp, rsp, length, 2); + int ret; + + cmd->cmd[0] =3D MAX77759_MAXQ_OPCODE_GPIO_CONTROL_READ; + + ret =3D max77759_maxq_command(chip->max77759, cmd, rsp); + if (ret < 0) + return ret; + + return rsp->rsp[1]; +} + +static int max77759_gpio_maxq_gpio_control_write(struct max77759_gpio_chip= *chip, + u8 ctrl) +{ + DEFINE_FLEX(struct max77759_maxq_command, cmd, cmd, length, 2); + + cmd->cmd[0] =3D MAX77759_MAXQ_OPCODE_GPIO_CONTROL_WRITE; + cmd->cmd[1] =3D ctrl; + + return max77759_maxq_command(chip->max77759, cmd, NULL); +} + +static int +max77759_gpio_direction_from_control(int ctrl, unsigned int offset) +{ + enum max77759_control_gpio_dir dir; + + dir =3D !!(ctrl & MAX77759_GPIOx_DIR_MASK(offset)); + return ((dir =3D=3D MAX77759_GPIO_DIR_OUT) + ? GPIO_LINE_DIRECTION_OUT + : GPIO_LINE_DIRECTION_IN); +} + +static int max77759_gpio_get_direction(struct gpio_chip *gc, + unsigned int offset) +{ + struct max77759_gpio_chip *chip =3D gpiochip_get_data(gc); + int ctrl; + + ctrl =3D max77759_gpio_maxq_gpio_control_read(chip); + if (ctrl < 0) + return ctrl; + + return max77759_gpio_direction_from_control(ctrl, offset); +} + +static int max77759_gpio_direction_helper(struct gpio_chip *gc, + unsigned int offset, + enum max77759_control_gpio_dir dir, + int value) +{ + struct max77759_gpio_chip *chip =3D gpiochip_get_data(gc); + int ctrl, new_ctrl; + + guard(mutex)(&chip->maxq_lock); + + ctrl =3D max77759_gpio_maxq_gpio_control_read(chip); + if (ctrl < 0) + return ctrl; + + new_ctrl =3D ctrl & ~MAX77759_GPIOx_DIR_MASK(offset); + new_ctrl |=3D MAX77759_GPIOx_DIR(offset, dir); + + if (dir =3D=3D MAX77759_GPIO_DIR_OUT) { + new_ctrl &=3D ~MAX77759_GPIOx_OUTVAL_MASK(offset); + new_ctrl |=3D MAX77759_GPIOx_OUTVAL(offset, value); + } + + if (new_ctrl =3D=3D ctrl) + return 0; + + return max77759_gpio_maxq_gpio_control_write(chip, new_ctrl); +} + +static int max77759_gpio_direction_input(struct gpio_chip *gc, + unsigned int offset) +{ + return max77759_gpio_direction_helper(gc, offset, + MAX77759_GPIO_DIR_IN, -1); +} + +static int max77759_gpio_direction_output(struct gpio_chip *gc, + unsigned int offset, int value) +{ + return max77759_gpio_direction_helper(gc, offset, + MAX77759_GPIO_DIR_OUT, value); +} + +static int max77759_gpio_get_value(struct gpio_chip *gc, unsigned int offs= et) +{ + struct max77759_gpio_chip *chip =3D gpiochip_get_data(gc); + int ctrl, mask; + + ctrl =3D max77759_gpio_maxq_gpio_control_read(chip); + if (ctrl < 0) + return ctrl; + + /* + * The input status bit doesn't reflect the pin state when the GPIO is + * configured as an output. Check the direction, and inspect the input + * or output bit accordingly. + */ + mask =3D ((max77759_gpio_direction_from_control(ctrl, offset) + =3D=3D GPIO_LINE_DIRECTION_IN) + ? MAX77759_GPIOx_INVAL_MASK(offset) + : MAX77759_GPIOx_OUTVAL_MASK(offset)); + + return !!(ctrl & mask); +} + +static int max77759_gpio_set_value(struct gpio_chip *gc, + unsigned int offset, int value) +{ + struct max77759_gpio_chip *chip =3D gpiochip_get_data(gc); + int ctrl, new_ctrl; + + guard(mutex)(&chip->maxq_lock); + + ctrl =3D max77759_gpio_maxq_gpio_control_read(chip); + if (ctrl < 0) + return ctrl; + + new_ctrl =3D ctrl & ~MAX77759_GPIOx_OUTVAL_MASK(offset); + new_ctrl |=3D MAX77759_GPIOx_OUTVAL(offset, value); + + if (new_ctrl =3D=3D ctrl) + return 0; + + return max77759_gpio_maxq_gpio_control_write(chip, new_ctrl); +} + +static void max77759_gpio_irq_mask(struct irq_data *d) +{ + struct gpio_chip *gc =3D irq_data_get_irq_chip_data(d); + struct max77759_gpio_chip *chip =3D gpiochip_get_data(gc); + irq_hw_number_t hwirq =3D irqd_to_hwirq(d); + + chip->irq_mask &=3D ~MAX77759_MAXQ_REG_UIC_INT1_GPIOxI_MASK(hwirq); + chip->irq_mask |=3D MAX77759_MAXQ_REG_UIC_INT1_GPIOxI(hwirq, 1); + chip->irq_mask_changed |=3D MAX77759_MAXQ_REG_UIC_INT1_GPIOxI(hwirq, 1); + + gpiochip_disable_irq(gc, hwirq); +} + +static void max77759_gpio_irq_unmask(struct irq_data *d) +{ + struct gpio_chip *gc =3D irq_data_get_irq_chip_data(d); + struct max77759_gpio_chip *chip =3D gpiochip_get_data(gc); + irq_hw_number_t hwirq =3D irqd_to_hwirq(d); + + gpiochip_enable_irq(gc, hwirq); + + chip->irq_mask &=3D ~MAX77759_MAXQ_REG_UIC_INT1_GPIOxI_MASK(hwirq); + chip->irq_mask |=3D MAX77759_MAXQ_REG_UIC_INT1_GPIOxI(hwirq, 0); + chip->irq_mask_changed |=3D MAX77759_MAXQ_REG_UIC_INT1_GPIOxI(hwirq, 1); +} + +static int max77759_gpio_set_irq_type(struct irq_data *d, unsigned int typ= e) +{ + struct gpio_chip *gc =3D irq_data_get_irq_chip_data(d); + struct max77759_gpio_chip *chip =3D gpiochip_get_data(gc); + irq_hw_number_t hwirq =3D irqd_to_hwirq(d); + + chip->irq_trig &=3D ~MAX77759_GPIOx_TRIGGER_MASK(hwirq); + switch (type) { + case IRQ_TYPE_EDGE_RISING: + chip->irq_trig |=3D MAX77759_GPIOx_TRIGGER(hwirq, + MAX77759_GPIO_TRIGGER_RISING); + break; + + case IRQ_TYPE_EDGE_FALLING: + chip->irq_trig |=3D MAX77759_GPIOx_TRIGGER(hwirq, + MAX77759_GPIO_TRIGGER_FALLING); + break; + + default: + return -EINVAL; + } + + chip->irq_trig_changed |=3D MAX77759_GPIOx_TRIGGER(hwirq, 1); + + return 0; +} + +static void max77759_gpio_bus_lock(struct irq_data *d) +{ + struct gpio_chip *gc =3D irq_data_get_irq_chip_data(d); + struct max77759_gpio_chip *chip =3D gpiochip_get_data(gc); + + mutex_lock(&chip->irq_lock); +} + +static int max77759_gpio_bus_sync_unlock_helper(struct gpio_chip *gc, + struct max77759_gpio_chip *chip) + __must_hold(&chip->maxq_lock) +{ + int ctrl, trigger, new_trigger, new_ctrl; + unsigned long irq_trig_changed; + int offset; + int ret; + + lockdep_assert_held(&chip->maxq_lock); + + ctrl =3D max77759_gpio_maxq_gpio_control_read(chip); + trigger =3D max77759_gpio_maxq_gpio_trigger_read(chip); + if (ctrl < 0 || trigger < 0) { + dev_err(gc->parent, "failed to read current state: %d / %d\n", + ctrl, trigger); + return (ctrl < 0) ? ctrl : trigger; + } + + new_trigger =3D trigger & ~chip->irq_trig_changed; + new_trigger |=3D (chip->irq_trig & chip->irq_trig_changed); + + /* change GPIO direction if required */ + new_ctrl =3D ctrl; + irq_trig_changed =3D chip->irq_trig_changed; + for_each_set_bit(offset, &irq_trig_changed, MAX77759_N_GPIOS) { + new_ctrl &=3D ~MAX77759_GPIOx_DIR_MASK(offset); + new_ctrl |=3D MAX77759_GPIOx_DIR(offset, MAX77759_GPIO_DIR_IN); + } + + if (new_trigger !=3D trigger) { + ret =3D max77759_gpio_maxq_gpio_trigger_write(chip, new_trigger); + if (ret) { + dev_err(gc->parent, + "failed to write new trigger: %d\n", ret); + return ret; + } + } + + if (new_ctrl !=3D ctrl) { + ret =3D max77759_gpio_maxq_gpio_control_write(chip, new_ctrl); + if (ret) { + dev_err(gc->parent, + "failed to write new control: %d\n", ret); + return ret; + } + } + + chip->irq_trig_changed =3D 0; + + return 0; +} + +static void max77759_gpio_bus_sync_unlock(struct irq_data *d) +{ + struct gpio_chip *gc =3D irq_data_get_irq_chip_data(d); + struct max77759_gpio_chip *chip =3D gpiochip_get_data(gc); + int ret; + + scoped_guard(mutex, &chip->maxq_lock) { + ret =3D max77759_gpio_bus_sync_unlock_helper(gc, chip); + if (ret) + goto out_unlock; + } + + ret =3D regmap_update_bits(chip->map, + MAX77759_MAXQ_REG_UIC_INT1_M, + chip->irq_mask_changed, chip->irq_mask); + if (ret) { + dev_err(gc->parent, + "failed to update UIC_INT1 irq mask: %d\n", ret); + goto out_unlock; + } + + chip->irq_mask_changed =3D 0; + +out_unlock: + mutex_unlock(&chip->irq_lock); +} + +static void max77759_gpio_irq_print_chip(struct irq_data *d, struct seq_fi= le *p) +{ + struct gpio_chip *gc =3D irq_data_get_irq_chip_data(d); + + seq_puts(p, dev_name(gc->parent)); +} + +static const struct irq_chip max77759_gpio_irq_chip =3D { + .irq_mask =3D max77759_gpio_irq_mask, + .irq_unmask =3D max77759_gpio_irq_unmask, + .irq_set_type =3D max77759_gpio_set_irq_type, + .irq_bus_lock =3D max77759_gpio_bus_lock, + .irq_bus_sync_unlock =3D max77759_gpio_bus_sync_unlock, + .irq_print_chip =3D max77759_gpio_irq_print_chip, + .flags =3D IRQCHIP_IMMUTABLE, + GPIOCHIP_IRQ_RESOURCE_HELPERS, +}; + +static irqreturn_t max77759_gpio_irqhandler(int irq, void *data) +{ + struct max77759_gpio_chip *chip =3D data; + struct gpio_chip *gc =3D &chip->gc; + bool handled =3D false; + + /* iterate until no interrupt is pending */ + while (true) { + unsigned int uic_int1; + int ret; + unsigned long pending; + int offset; + + ret =3D regmap_read(chip->map, MAX77759_MAXQ_REG_UIC_INT1, + &uic_int1); + if (ret < 0) { + dev_err_ratelimited(gc->parent, + "failed to read IRQ status: %d\n", + ret); + /* + * If !handled, we have looped not even once, which + * means we should return IRQ_NONE in that case (and + * of course IRQ_HANDLED otherwise). + */ + return IRQ_RETVAL(handled); + } + + pending =3D uic_int1; + pending &=3D (MAX77759_MAXQ_REG_UIC_INT1_GPIO6I + | MAX77759_MAXQ_REG_UIC_INT1_GPIO5I); + if (!pending) + break; + + for_each_set_bit(offset, &pending, MAX77759_N_GPIOS) { + /* + * ACK interrupt by writing 1 to bit 'offset', all + * others need to be written as 0. This needs to be + * done unconditionally hence regmap_set_bits() is + * inappropriate here. + */ + regmap_write(chip->map, MAX77759_MAXQ_REG_UIC_INT1, + BIT(offset)); + + handle_nested_irq(irq_find_mapping(gc->irq.domain, + offset)); + + handled =3D true; + } + } + + return IRQ_RETVAL(handled); +} + +static int max77759_gpio_probe(struct platform_device *pdev) +{ + struct max77759_gpio_chip *chip; + int irq; + struct gpio_irq_chip *girq; + int ret; + unsigned long irq_flags; + struct irq_data *irqd; + + chip =3D devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->map =3D dev_get_regmap(pdev->dev.parent, "maxq"); + if (!chip->map) + return dev_err_probe(&pdev->dev, -ENODEV, "Missing regmap\n"); + + irq =3D platform_get_irq_byname(pdev, "GPI"); + if (irq < 0) + return dev_err_probe(&pdev->dev, irq, "Failed to get IRQ\n"); + + chip->max77759 =3D dev_get_drvdata(pdev->dev.parent); + ret =3D devm_mutex_init(&pdev->dev, &chip->maxq_lock); + if (ret) + return ret; + ret =3D devm_mutex_init(&pdev->dev, &chip->irq_lock); + if (ret) + return ret; + + chip->gc.base =3D -1; + chip->gc.label =3D dev_name(&pdev->dev); + chip->gc.parent =3D &pdev->dev; + chip->gc.can_sleep =3D true; + + chip->gc.names =3D max77759_gpio_line_names; + chip->gc.ngpio =3D MAX77759_N_GPIOS; + chip->gc.get_direction =3D max77759_gpio_get_direction; + chip->gc.direction_input =3D max77759_gpio_direction_input; + chip->gc.direction_output =3D max77759_gpio_direction_output; + chip->gc.get =3D max77759_gpio_get_value; + chip->gc.set_rv =3D max77759_gpio_set_value; + + girq =3D &chip->gc.irq; + gpio_irq_chip_set_chip(girq, &max77759_gpio_irq_chip); + /* This will let us handle the parent IRQ in the driver */ + girq->parent_handler =3D NULL; + girq->num_parents =3D 0; + girq->parents =3D NULL; + girq->default_type =3D IRQ_TYPE_NONE; + girq->handler =3D handle_simple_irq; + girq->threaded =3D true; + + ret =3D devm_gpiochip_add_data(&pdev->dev, &chip->gc, chip); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, + "Failed to add GPIO chip\n"); + + irq_flags =3D IRQF_ONESHOT | IRQF_SHARED; + irqd =3D irq_get_irq_data(irq); + if (irqd) + irq_flags |=3D irqd_get_trigger_type(irqd); + + ret =3D devm_request_threaded_irq(&pdev->dev, irq, NULL, + max77759_gpio_irqhandler, irq_flags, + dev_name(&pdev->dev), chip); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, + "Failed to request IRQ\n"); + + return ret; +} + +static const struct of_device_id max77759_gpio_of_id[] =3D { + { .compatible =3D "maxim,max77759-gpio", }, + { } +}; +MODULE_DEVICE_TABLE(of, max77759_gpio_of_id); + +static const struct platform_device_id max77759_gpio_platform_id[] =3D { + { "max77759-gpio", }, + { } +}; +MODULE_DEVICE_TABLE(platform, max77759_gpio_platform_id); + +static struct platform_driver max77759_gpio_driver =3D { + .driver =3D { + .name =3D "max77759-gpio", + .probe_type =3D PROBE_PREFER_ASYNCHRONOUS, + .of_match_table =3D max77759_gpio_of_id, + }, + .probe =3D max77759_gpio_probe, + .id_table =3D max77759_gpio_platform_id, +}; + +module_platform_driver(max77759_gpio_driver); + +MODULE_AUTHOR("Andr=C3=A9 Draszik "); +MODULE_DESCRIPTION("GPIO driver for Maxim MAX77759"); +MODULE_LICENSE("GPL"); --=20 2.49.0.901.g37484f566f-goog