From nobody Tue Feb 10 00:21:36 2026 Received: from mail-ej1-f45.google.com (mail-ej1-f45.google.com [209.85.218.45]) (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 8EF60267AF6 for ; Mon, 28 Apr 2025 11:36:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.218.45 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1745840176; cv=none; b=dBAI1BtBBnY7xcMO9eAu/349EfKCehlN2PqtOvWiY/2JraEM6LLzca39wBqwASMDCx1Oq5b/yhfB9weDqh4+XVgmeIygF3GGZhbqjydtGoSOvIDIzUMsh6IVe97X3hYSIRY9LTRCdY3HkTvqUsY1HlIZM4bcqLB7dhk8gWV95rI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1745840176; c=relaxed/simple; bh=XyNvxHnB/PUwPDNvfHYgExTxSFiaRZGSU0Wz4h3ddm0=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=hSAg66LCUoQIM9szbsjDy0o9+kSu7RHPRwaeCwdrFoRli8lQHljWKH5iXYqtm47Ay6QB0as3CNKASDdXmCruFk1wIkAPSJxEGDvAhSEe5Ed3TmfVVa/cklqygULZUPUNnz0wun0DH7ffr4GEQsCo0p2IEHncNVxbYsbNj50uee0= 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=qcXGx+t8; arc=none smtp.client-ip=209.85.218.45 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="qcXGx+t8" Received: by mail-ej1-f45.google.com with SMTP id a640c23a62f3a-ac34257295dso793489666b.2 for ; Mon, 28 Apr 2025 04:36:11 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1745840170; x=1746444970; 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=wlmXsLPTvTC55KpNUdY8vaigZc8QVDvQGQci8hOP1Fw=; b=qcXGx+t8uke1HI8qWc1aKbxz7ikkVoQOzBZ+3OEQ/QrYS4fWzSFcZPIGTsNDBzBsM3 utXR6GaUMB7ZqW+n3ATzpjaAu0naVwSnAYZAndio274TF760S6t7B8OrL3tA/r1O+8SO jJmnrtaZgT6QW+4XJ53/KE/Gzd+GUy0v31ubBkbNku2UOu1RRNQhKH9YWux5wm3r+oe1 TXGqS7UAx1vqufoGESleMhRjrga8nIUr2Z2nxIPxPRFN0iXtKLC7Auv2pwg/cfvksziU 0MgmpqLV8mhxE8tI1p5VwNusMDIu9tq9GwstT+syOa0vtXWObh3zTZHMnos7qvYiRHvJ cQkQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1745840170; x=1746444970; 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=wlmXsLPTvTC55KpNUdY8vaigZc8QVDvQGQci8hOP1Fw=; b=JS9YiQokFYNedPZkUZ2rvVnujUZuaqF8XcEyWtMx/k0/NsKB8ww/mfurnuj0Uu/u8K S2F8GL/Oip5AbgtazvIxvJw1jlTJLDSF30xvN1CdfbFtqydozcGyVMDapu330MyZjm9Y qAnU+D02K0sVUpt8IPXlxEDDzhWAaWHqe0LMb5leK6QyeAjhN4s6LaLk4gVO3xw4CMeC eWNS/6xuFJH9Y2g8yRH7YvbUrv1v8HAsvTmPusGCO1Qok12D4MlHgjC3oScp9Pgwmo/0 7prtzVG4APbKZdqWM4BEhTK+dBNiitYCt+hBrePsrGl7E7h6+1j7zoAX8vNX1vom6iTn yVxg== X-Forwarded-Encrypted: i=1; AJvYcCVatyNCzVBM6gbBG+BSMWtxJZhiA5dzEpI2B6AXHGzolX07Zj5fIv/ELlxMow1XsdQ87cQJWuOksWQ9Mx0=@vger.kernel.org X-Gm-Message-State: AOJu0YwzZaF/06L3MeANM3NhTWfezPorh3tZ/F1hVrW8RjMo+NnABcYZ xZgQvZXxQLEaDjYLbsjxQRwrbDwXZZno1vPCLSKD77Ge6peQ6adKsYlNZVh6cgM= X-Gm-Gg: ASbGncsYvmmeLaez0IPHERRKNEfb2on5WJ27YH0xsoh3cgXjSE9aCyIQbSJ5PT61ze8 O1mVjsRPHneHfvA0Pc6xG5BpZ1yPb0e8578q2/cLHy6M4tYOSTccvKHvZ9D+Lo1MNZA5VSB0mmj 6QzHdnZeYKTv9wjpHLh6fLWs/u00nT1YkQvtXKW0WWZ4nS+KPR2Mm2lZq2D0D3vNgcl/wxA7KvI tFvGNQeoWxeqX65cntgDWz8yp84j0p5dkWhFGq6G+Z8BduBw6nG7sdPxndRXZW9DXmbWmCYHmGV E5vqLq4i8NRyhIFIhJ7X7gt1CrhWAwStdHqbxL+EMUCM7dHFfEL1QDoeDaQA6eUy6o9EgVXy3jP F6OHB7YpmYLloLiHDkdSPiLbk X-Google-Smtp-Source: AGHT+IGIHiNHKJekY/wM4UV4Zz5ICJ8vY/cXtlCv3nQ5hKl701WSu8uKvMDcQbX9hPCsyLG0YfWJhw== X-Received: by 2002:a17:907:86a4:b0:aca:d2f0:d291 with SMTP id a640c23a62f3a-ace7104d411mr1005606066b.5.1745840170186; Mon, 28 Apr 2025 04:36:10 -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-ace6edb1580sm619937766b.175.2025.04.28.04.36.09 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 28 Apr 2025 04:36:09 -0700 (PDT) From: =?utf-8?q?Andr=C3=A9_Draszik?= Date: Mon, 28 Apr 2025 12:36:08 +0100 Subject: [PATCH v7 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: <20250428-max77759-mfd-v7-5-edfe40c16fe8@linaro.org> References: <20250428-max77759-mfd-v7-0-edfe40c16fe8@linaro.org> In-Reply-To: <20250428-max77759-mfd-v7-0-edfe40c16fe8@linaro.org> To: Lee Jones , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Linus Walleij , Bartosz Golaszewski , Kees Cook , "Gustavo A. R. Silva" , Srinivas Kandagatla 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?= , Bartosz Golaszewski 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. Reviewed-by: Linus Walleij Acked-by: Bartosz Golaszewski Signed-off-by: Andr=C3=A9 Draszik --- 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 | 524 +++++++++++++++++++++++++++++++++++++++= ++++ 4 files changed, 539 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 64b77fd6be4fe2e994822eb7e071c95a15bd4a97..8ec9a09bed546fd9033a7cb977a= c3c0533edbb30 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14661,6 +14661,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..09423ad5ae22d1046189bcda961= ca35c828c6f21 --- /dev/null +++ b/drivers/gpio/gpio-max77759.c @@ -0,0 +1,524 @@ +// 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 void 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; + + new_ctrl =3D ctrl & ~MAX77759_GPIOx_OUTVAL_MASK(offset); + new_ctrl |=3D MAX77759_GPIOx_OUTVAL(offset, value); + + if (new_ctrl =3D=3D ctrl) + 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 =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 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, +}; + +module_platform_driver(max77759_gpio_driver); + +MODULE_AUTHOR("Andr=C3=A9 Draszik "); +MODULE_DESCRIPTION("GPIO driver for Maxim MAX77759"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:max77759-gpio"); --=20 2.49.0.850.g28803427d3-goog