From patchwork Thu Jul 7 18:10:03 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: William Breathitt Gray X-Patchwork-Id: 589034 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id E14BCC43334 for ; Thu, 7 Jul 2022 23:16:18 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236058AbiGGXQS (ORCPT ); Thu, 7 Jul 2022 19:16:18 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58168 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236128AbiGGXQR (ORCPT ); Thu, 7 Jul 2022 19:16:17 -0400 Received: from mail-oa1-x2b.google.com (mail-oa1-x2b.google.com [IPv6:2001:4860:4864:20::2b]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 6DAF327FD5 for ; Thu, 7 Jul 2022 16:16:16 -0700 (PDT) Received: by mail-oa1-x2b.google.com with SMTP id 586e51a60fabf-10c0119dd16so16263437fac.6 for ; Thu, 07 Jul 2022 16:16:16 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=ael3to+pQw5kcMQjn9uyLd/aGdcqjP4ufwXWigGWHxA=; b=HyeCcX6zfK6ju/UZMlCfR3G3aJWHIFLO/qUunbC7r3xRAQHFGUyLqpXRPpgHA6v4BB TIAI4+U2VgOA5hJvi5+hVzLwlTur0Dif/cOWQoMaBoulErlejKxcJP3drjVKAR9+qnAz T6Rvw8z7ZcZLkHrSyFToTMAzoIeT+ubDoO9qpoYCBgv3wp6ua5hX0sXsaLpAWM33d4pQ QdBGm1oGDDBDc0fwxLOthSdHqmvLvFOhgqD0hjYuJya8uGefPHzxtxcO4yl3FvU40Wc2 ZQJz08Xlq1uO5a7eFt+VEPWotaxCtuOMcUV4ZagZoaQxpA7/mXVqwtYBXHXhty/v6spm PrYA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=ael3to+pQw5kcMQjn9uyLd/aGdcqjP4ufwXWigGWHxA=; b=Pv0015jQ+XHYFJjkLoEzzrbC7FVi9pWTYOy82iBNNpYolEoZEW7e7Fyy2LMEqY+2bz KKDHkyH6fS/hCGbyrIS5VVR9pmrA+QrmrqAJ2zN8zmmp0eWtQXThpN4/7DpSkVa0mMZ6 Y85gaOwEpO5LCowj2zfGBHm+SjgXqhJWQkdbVHVACAo0G6fHGvfd7ulQuXfUifyaxT8e penREfX10nsriWdjY02kaPyjOt0mKbOpf8A3ZtafzNmlFGoUPBKgDdJkBZ3rBiXVxjXw FPXUr2RTNAnNdLCC30L8pMOQ48GSvExkBl4m8TSMNoioSVpa/+G1SXyhNyDEnlihTHXn FoVQ== X-Gm-Message-State: AJIora/WM1NPHm2t8+cIjsGzzkCgua3FInJ9nzGuCzR84ECjkSdwHVzp JoOnUVcJfOANObIz3cj/x1tOew== X-Google-Smtp-Source: AGRyM1tlNmSH+B4wMw15/nh8eWfTqZLqfQBVqo7VMlmTROC9pe6veZ5fGU6Xoj6Mey5fT0e79FhFvQ== X-Received: by 2002:a05:6871:611:b0:10c:4bc0:88b9 with SMTP id w17-20020a056871061100b0010c4bc088b9mr228790oan.81.1657235775988; Thu, 07 Jul 2022 16:16:15 -0700 (PDT) Received: from fedora.attlocal.net (69-109-179-158.lightspeed.dybhfl.sbcglobal.net. [69.109.179.158]) by smtp.gmail.com with ESMTPSA id cg10-20020a056830630a00b006190efaf118sm2177606otb.66.2022.07.07.16.16.15 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 07 Jul 2022 16:16:15 -0700 (PDT) From: William Breathitt Gray To: linus.walleij@linaro.org, brgl@bgdev.pl Cc: linux-gpio@vger.kernel.org, linux-kernel@vger.kernel.org, William Breathitt Gray , Fred Eckert , John Hentges , Jay Dolan Subject: [PATCH v2 1/6] gpio: i8255: Introduce the i8255 module Date: Thu, 7 Jul 2022 14:10:03 -0400 Message-Id: <6be749842a4ad629c8697101f170dc7e425ae082.1657216200.git.william.gray@linaro.org> X-Mailer: git-send-email 2.36.1 In-Reply-To: References: MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-gpio@vger.kernel.org Exposes consumer functions providing support for Intel 8255 Programmable Peripheral Interface devices. A CONFIG_GPIO_I8255 Kconfig option is introduced; modules wanting access to these functions should select this Kconfig option. Tested-by: Fred Eckert Cc: John Hentges Cc: Jay Dolan Signed-off-by: William Breathitt Gray --- MAINTAINERS | 6 + drivers/gpio/Kconfig | 3 + drivers/gpio/Makefile | 1 + drivers/gpio/gpio-i8255.c | 249 +++++++++++++++++++++++++++++++++++++ include/linux/gpio/i8255.h | 34 +++++ 5 files changed, 293 insertions(+) create mode 100644 drivers/gpio/gpio-i8255.c create mode 100644 include/linux/gpio/i8255.h diff --git a/MAINTAINERS b/MAINTAINERS index a6d3bd9d2a8d..c4ae792a8a43 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9799,6 +9799,12 @@ L: linux-fbdev@vger.kernel.org S: Maintained F: drivers/video/fbdev/i810/ +INTEL 8255 GPIO DRIVER +M: William Breathitt Gray +L: linux-gpio@vger.kernel.org +S: Maintained +F: drivers/gpio/gpio-i8255.c + INTEL ASoC DRIVERS M: Cezary Rojewski M: Pierre-Louis Bossart diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index b01961999ced..5cbe93330213 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -829,6 +829,9 @@ endmenu menu "Port-mapped I/O GPIO drivers" depends on X86 # Unconditional I/O space access +config GPIO_I8255 + tristate + config GPIO_104_DIO_48E tristate "ACCES 104-DIO-48E GPIO support" depends on PC104 diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 14352f6dfe8e..06057e127949 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -67,6 +67,7 @@ obj-$(CONFIG_GPIO_GW_PLD) += gpio-gw-pld.o obj-$(CONFIG_GPIO_HISI) += gpio-hisi.o obj-$(CONFIG_GPIO_HLWD) += gpio-hlwd.o obj-$(CONFIG_HTC_EGPIO) += gpio-htc-egpio.o +obj-$(CONFIG_GPIO_I8255) += gpio-i8255.o obj-$(CONFIG_GPIO_ICH) += gpio-ich.o obj-$(CONFIG_GPIO_IDT3243X) += gpio-idt3243x.o obj-$(CONFIG_GPIO_IOP) += gpio-iop.o diff --git a/drivers/gpio/gpio-i8255.c b/drivers/gpio/gpio-i8255.c new file mode 100644 index 000000000000..ef43312015f4 --- /dev/null +++ b/drivers/gpio/gpio-i8255.c @@ -0,0 +1,249 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Intel 8255 Programmable Peripheral Interface + * Copyright (C) 2022 William Breathitt Gray + */ +#include +#include +#include +#include +#include +#include +#include + +#define I8255_CONTROL_PORTCLOWER_DIRECTION BIT(0) +#define I8255_CONTROL_PORTB_DIRECTION BIT(1) +#define I8255_CONTROL_PORTCUPPER_DIRECTION BIT(3) +#define I8255_CONTROL_PORTA_DIRECTION BIT(4) +#define I8255_CONTROL_MODE_SET BIT(7) +#define I8255_PORTA 0 +#define I8255_PORTB 1 +#define I8255_PORTC 2 + +static int i8255_get_port(const struct i8255 __iomem *const ppi, + const unsigned long io_port, const unsigned long mask) +{ + const unsigned long group = io_port / 3; + const unsigned long ppi_port = io_port % 3; + + return ioread8(&ppi[group].port[ppi_port]) & mask; +} + +static u8 i8255_direction_mask(const unsigned long offset) +{ + const unsigned long io_port = offset / 8; + const unsigned long ppi_port = io_port % 3; + + switch (ppi_port) { + case I8255_PORTA: + return I8255_CONTROL_PORTA_DIRECTION; + case I8255_PORTB: + return I8255_CONTROL_PORTB_DIRECTION; + case I8255_PORTC: + /* Port C can be configured by nibble */ + if (offset % 8 > 3) + return I8255_CONTROL_PORTCUPPER_DIRECTION; + return I8255_CONTROL_PORTCLOWER_DIRECTION; + default: + /* Should never reach this path */ + return 0; + } +} + +static void i8255_set_port(struct i8255 __iomem *const ppi, + const unsigned long io_port, + const unsigned long io_mask, + const unsigned long bit_mask) +{ + const unsigned long group = io_port / 3; + const unsigned long ppi_port = io_port % 3; + unsigned long out_state; + + out_state = ioread8(&ppi[group].port[ppi_port]); + out_state &= ~io_mask; + out_state |= bit_mask; + + iowrite8(out_state, &ppi[group].port[ppi_port]); +} + +/** + * i8255_direction_input - configure signal offset as input + * @ppi: Intel 8255 Programmable Peripheral Interface groups + * @control_state: control register states of the respective PPI groups + * @offset: signal offset to configure as input + * + * Configures a signal @offset as input for the respective Intel 8255 + * Programmable Peripheral Interface (@ppi) groups. The @control_state values + * are updated to reflect the new configuration. + */ +void i8255_direction_input(struct i8255 __iomem *const ppi, + u8 *const control_state, const unsigned long offset) +{ + const unsigned long io_port = offset / 8; + const unsigned long group = io_port / 3; + + control_state[group] |= I8255_CONTROL_MODE_SET; + control_state[group] |= i8255_direction_mask(offset); + + iowrite8(control_state[group], &ppi[group].control); +} +EXPORT_SYMBOL_GPL(i8255_direction_input); + +/** + * i8255_direction_output - configure signal offset as output + * @control_state: control register states of the respective PPI groups + * @ppi: Intel 8255 Programmable Peripheral Interface groups + * @offset: signal offset to configure as output + * @value: signal value to output + * + * Configures a signal @offset as output for the respective Intel 8255 + * Programmable Peripheral Interface (@ppi) groups and sets the respective + * signal output to the desired @value. The @control_state values are updated to + * reflect the new configuration. + */ +void i8255_direction_output(struct i8255 __iomem *const ppi, + u8 *const control_state, const unsigned long offset, + const unsigned long value) +{ + const unsigned long io_port = offset / 8; + const unsigned long group = io_port / 3; + + control_state[group] |= I8255_CONTROL_MODE_SET; + control_state[group] &= ~i8255_direction_mask(offset); + + iowrite8(control_state[group], &ppi[group].control); + i8255_set(ppi, offset, value); +} +EXPORT_SYMBOL_GPL(i8255_direction_output); + +/** + * i8255_get - get signal value at signal offset + * @ppi: Intel 8255 Programmable Peripheral Interface groups + * @offset: offset of signal to get + * + * Returns the signal value (0=low, 1=high) for the signal at @offset for the + * respective Intel 8255 Programmable Peripheral Interface (@ppi) groups. + */ +int i8255_get(const struct i8255 __iomem *const ppi, const unsigned long offset) +{ + const unsigned long io_port = offset / 8; + const unsigned long offset_mask = BIT(offset % 8); + + return !!i8255_get_port(ppi, io_port, offset_mask); +} +EXPORT_SYMBOL_GPL(i8255_get); + +/** + * i8255_get_direction - get the I/O direction for a signal offset + * @control_state: control register states of the respective PPI groups + * @offset: offset of signal to get direction + * + * Returns the signal direction (0=output, 1=input) for the signal at @offset. + * groups. + */ +int i8255_get_direction(const u8 *const control_state, + const unsigned long offset) +{ + const unsigned long io_port = offset / 8; + const unsigned long group = io_port / 3; + + return !!(control_state[group] & i8255_direction_mask(offset)); +} +EXPORT_SYMBOL_GPL(i8255_get_direction); + +/** + * i8255_get_multiple - get multiple signal values at multiple signal offsets + * @ppi: Intel 8255 Programmable Peripheral Interface groups + * @mask: mask of signals to get + * @bits: bitmap to store signal values + * @ngpio: number of GPIO signals of the respective PPI groups + * + * Stores in @bits the values (0=low, 1=high) for the signals defined by @mask + * for the respective Intel 8255 Programmable Peripheral Interface (@ppi) + * groups. + */ +void i8255_get_multiple(const struct i8255 __iomem *const ppi, + const unsigned long *const mask, + unsigned long *const bits, const unsigned long ngpio) +{ + unsigned long offset; + unsigned long gpio_mask; + unsigned long io_port; + unsigned long port_state; + + bitmap_zero(bits, ngpio); + + for_each_set_clump8(offset, gpio_mask, mask, ngpio) { + io_port = offset / 8; + port_state = i8255_get_port(ppi, io_port, gpio_mask); + + bitmap_set_value8(bits, port_state, offset); + } +} +EXPORT_SYMBOL_GPL(i8255_get_multiple); + +/** + * i8255_mode0_output - configure all PPI ports to MODE 0 output mode + * @ppi: Intel 8255 Programmable Peripheral Interface group + * + * Configures all Intel 8255 Programmable Peripheral Interface (@ppi) ports to + * MODE 0 (Basic Input/Output) output mode. + */ +void i8255_mode0_output(struct i8255 __iomem *const ppi) +{ + iowrite8(I8255_CONTROL_MODE_SET, &ppi->control); +} +EXPORT_SYMBOL_GPL(i8255_mode0_output); + +/** + * i8255_set - set signal value at signal offset + * @ppi: Intel 8255 Programmable Peripheral Interface groups + * @offset: offset of signal to set + * @value: value of signal to set + * + * Assigns output @value for the signal at @offset for the respective Intel 8255 + * Programmable Peripheral Interface (@ppi) groups. + */ +void i8255_set(struct i8255 __iomem *const ppi, const unsigned long offset, + const unsigned long value) +{ + const unsigned long io_port = offset / 8; + const unsigned long port_offset = offset % 8; + const unsigned long offset_mask = BIT(port_offset); + const unsigned long bit_mask = value << port_offset; + + i8255_set_port(ppi, io_port, offset_mask, bit_mask); +} +EXPORT_SYMBOL_GPL(i8255_set); + +/** + * i8255_set_multiple - set signal values at multiple signal offsets + * @ppi: Intel 8255 Programmable Peripheral Interface groups + * @mask: mask of signals to set + * @bits: bitmap of signal output values + * @ngpio: number of GPIO signals of the respective PPI groups + * + * Assigns output values defined by @bits for the signals defined by @mask for + * the respective Intel 8255 Programmable Peripheral Interface (@ppi) groups. + */ +void i8255_set_multiple(struct i8255 __iomem *const ppi, + const unsigned long *const mask, + const unsigned long *const bits, + const unsigned long ngpio) +{ + unsigned long offset; + unsigned long gpio_mask; + unsigned long bit_mask; + unsigned long io_port; + + for_each_set_clump8(offset, gpio_mask, mask, ngpio) { + bit_mask = bitmap_get_value8(bits, offset) & gpio_mask; + io_port = offset / 8; + i8255_set_port(ppi, io_port, gpio_mask, bit_mask); + } +} +EXPORT_SYMBOL_GPL(i8255_set_multiple); + +MODULE_AUTHOR("William Breathitt Gray"); +MODULE_DESCRIPTION("Intel 8255 Programmable Peripheral Interface"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/gpio/i8255.h b/include/linux/gpio/i8255.h new file mode 100644 index 000000000000..7ddcf7fcd1dd --- /dev/null +++ b/include/linux/gpio/i8255.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright 2022 William Breathitt Gray */ +#ifndef _I8255_H_ +#define _I8255_H_ + +#include +#include + +/** + * struct i8255 - Intel 8255 register structure + * @port: Port A, B, and C + * @control: Control register + */ +struct i8255 { + u8 port[3]; + u8 control; +}; + +void i8255_direction_input(struct i8255 __iomem *ppi, u8 *control_state, + unsigned long offset); +void i8255_direction_output(struct i8255 __iomem *ppi, u8 *control_state, + unsigned long offset, unsigned long value); +int i8255_get(const struct i8255 __iomem *ppi, unsigned long offset); +int i8255_get_direction(const u8 *control_state, unsigned long offset); +void i8255_get_multiple(const struct i8255 __iomem *ppi, + const unsigned long *mask, unsigned long *bits, + unsigned long ngpio); +void i8255_mode0_output(struct i8255 __iomem *const ppi); +void i8255_set(struct i8255 __iomem *ppi, unsigned long offset, + unsigned long value); +void i8255_set_multiple(struct i8255 __iomem *ppi, const unsigned long *mask, + const unsigned long *bits, unsigned long ngpio); + +#endif /* _I8255_H_ */