linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC 0/3] Add support control UP board CPLD/FPGA pin control
@ 2022-12-07 16:33 larry.lai
  2022-12-07 16:33 ` [RFC 1/3] mfd: Add support for UP board CPLD/FPGA larry.lai
                   ` (4 more replies)
  0 siblings, 5 replies; 12+ messages in thread
From: larry.lai @ 2022-12-07 16:33 UTC (permalink / raw)
  To: lee, andriy.shevchenko, linus.walleij, pavel
  Cc: linux-kernel, linux-gpio, linux-leds, GaryWang, musa.lin,
	jack.chang, noah.hung, larry.lai

The UP board <http://www.upboard.com> is the computer board for 
Professional Makers and Industrial Applications. We want to upstream 
the UP board 40-pin GP-bus Kernel driver for giving the users better 
experience on the software release. (not just download from UP board 
github)

These patches are generated from the Linux kernel mainline tag v6.0.

larry.lai (3):
  mfd: Add support for UP board CPLD/FPGA
  pinctrl: Add support pin control for UP board CPLD/FPGA
  leds: Add support for UP board CPLD onboard LEDS

 drivers/leds/Kconfig              |   10 +
 drivers/leds/Makefile             |    1 +
 drivers/leds/leds-upboard.c       |   79 ++
 drivers/mfd/Kconfig               |   12 +
 drivers/mfd/Makefile              |    1 +
 drivers/mfd/upboard-fpga.c        |  669 ++++++++++++++
 drivers/pinctrl/Kconfig           |   14 +
 drivers/pinctrl/Makefile          |    1 +
 drivers/pinctrl/pinctrl-upboard.c | 1384 +++++++++++++++++++++++++++++
 include/linux/mfd/upboard-fpga.h  |   58 ++
 10 files changed, 2229 insertions(+)
 create mode 100644 drivers/leds/leds-upboard.c
 create mode 100644 drivers/mfd/upboard-fpga.c
 create mode 100644 drivers/pinctrl/pinctrl-upboard.c
 create mode 100644 include/linux/mfd/upboard-fpga.h


base-commit: 4fe89d07dcc2804c8b562f6c7896a45643d34b2f
-- 
2.17.1


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [RFC 1/3] mfd: Add support for UP board CPLD/FPGA
  2022-12-07 16:33 [RFC 0/3] Add support control UP board CPLD/FPGA pin control larry.lai
@ 2022-12-07 16:33 ` larry.lai
  2022-12-07 21:10   ` Andy Shevchenko
  2022-12-07 16:33 ` [RFC 2/3] pinctrl: Add support pin control " larry.lai
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 12+ messages in thread
From: larry.lai @ 2022-12-07 16:33 UTC (permalink / raw)
  To: lee, andriy.shevchenko, linus.walleij, pavel
  Cc: linux-kernel, linux-gpio, linux-leds, GaryWang, musa.lin,
	jack.chang, noah.hung, larry.lai, Javier Arteaga, Nicola Lunghi

The UP Squared board <http://www.upboard.com> implements certain
features (pin control, onboard LEDs or CEC) through an on-board CPLD/FPGA.

This mfd driver implements the line protocol to read and write registers
from the FPGA through regmap. The register address map is also included.

The UP Boards provide a few I/O pin headers (for both GPIO and
functions), including a 40-pin Raspberry Pi compatible header.

This patch implements support for the FPGA-based pin controller that
manages direction and enable state for those header pins.

Partial support UP boards:
* UP core + CREX
* UP core + CRST02

Signed-off-by: Javier Arteaga <javier@emutex.com>
[merge various fixes]
Signed-off-by: Nicola Lunghi <nicola.lunghi@emutex.com>
Reviewed-by: Lee Jones <lee@kernel.org>
Signed-off-by: larry.lai <larry.lai@yunjingtech.com>
---
PATCH V3 -> RFC 2022/11/23:
(1) Refer 2022/11/16 Lee Jones review, cleaned up coding style and
addressed review comments.
(2) Description on the UP Boards FPGA register read/write protocols
PATCH V2 -> V3:
(1) fixed kernel test robot compiler warning
PATCH V1 -> V2:
(1) Synchronizing upboard github to rc2
(2) Refer 2022/10/31 Lee Jones review, fixed some of the issues.
---
 drivers/mfd/Kconfig              |  12 +
 drivers/mfd/Makefile             |   1 +
 drivers/mfd/upboard-fpga.c       | 669 +++++++++++++++++++++++++++++++
 include/linux/mfd/upboard-fpga.h |  58 +++
 4 files changed, 740 insertions(+)
 create mode 100644 drivers/mfd/upboard-fpga.c
 create mode 100644 include/linux/mfd/upboard-fpga.h

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index abb58ab1a1a4..1041e937fc7a 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -2104,6 +2104,18 @@ config MFD_QCOM_PM8008
 	  under it in the device tree. Additional drivers must be enabled in
 	  order to use the functionality of the device.
 
+config MFD_INTEL_UPBOARD_FPGA
+	tristate "Support for the Intel platform foundation kit UP board FPGA"
+	select MFD_CORE
+	depends on X86 && ACPI
+	help
+	  Select this option to enable the Intel AAEON UP and UP^2 on-board FPGA.
+	  This is core driver for the UP board that implements certain (pin
+	  control, onboard LEDs or CEC) through an on-board FPGA.
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called upboard-fpga.
+
 menu "Multimedia Capabilities Port drivers"
 	depends on ARCH_SA1100
 
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 858cacf659d6..8374a05f6f43 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -250,6 +250,7 @@ obj-$(CONFIG_MFD_ALTERA_A10SR)	+= altera-a10sr.o
 obj-$(CONFIG_MFD_ALTERA_SYSMGR) += altera-sysmgr.o
 obj-$(CONFIG_MFD_STPMIC1)	+= stpmic1.o
 obj-$(CONFIG_MFD_SUN4I_GPADC)	+= sun4i-gpadc.o
+obj-$(CONFIG_MFD_INTEL_UPBOARD_FPGA)	+= upboard-fpga.o
 
 obj-$(CONFIG_MFD_STM32_LPTIMER)	+= stm32-lptimer.o
 obj-$(CONFIG_MFD_STM32_TIMERS) 	+= stm32-timers.o
diff --git a/drivers/mfd/upboard-fpga.c b/drivers/mfd/upboard-fpga.c
new file mode 100644
index 000000000000..123c17b5593e
--- /dev/null
+++ b/drivers/mfd/upboard-fpga.c
@@ -0,0 +1,669 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Intel AAEON UP Board series platform core driver
+ * and FPGA configuration support
+ *
+ * Copyright (c) 2017, Emutex Ltd. All rights reserved.
+ * Copyright (c) 2022, YunJingTech Ltd.
+ *
+ * Author: Javier Arteaga <javier@emutex.com>
+ */
+
+#include <linux/acpi.h>
+#include <linux/dmi.h>
+#include <linux/gpio.h>
+#include <linux/kernel.h>
+#include <linux/leds.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/upboard-fpga.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+struct upboard_fpga_data {
+	const struct regmap_config *regmapconf;
+	const struct mfd_cell *cells;
+	size_t ncells;
+};
+
+#define AAEON_MANUFACTURER_ID		0x01
+#define SUPPORTED_FW_MAJOR		0x0
+#define MENUFACTURER_ID_MASK		0xFF
+
+#define FIRMWARE_ID_BUILD_OFFSET	12
+#define FIRMWARE_ID_MAJOR_OFFSET	8
+#define FIRMWARE_ID_MINOR_OFFSET	4
+#define FIRMWARE_ID_PATCH_OFFSET	0
+#define FIRMWARE_ID_MASK		0xF
+
+#define UPFPGA_QUIRK_UNINITIALISED	BIT(0)
+#define UPFPGA_QUIRK_HRV1_IS_PROTO2	BIT(1)
+#define UPFPGA_QUIRK_GPIO_LED		BIT(2)
+
+/* Apollo Lake GPIO pin number mapping to FPGA LED */
+#define APL_GPIO_218			507
+
+/* For UP Board Series FPGA register read/write protocols 		      */
+/* EMUTEX specs: 						              */
+/* D0   D1  D2  D3  D4  D5  D6  D7  D8  D9 .... D22  D23                      */
+/* [RW][        address           ][	    DATA        ]                     */
+
+/* Read Sequence:                                                             */
+/*      ___   ____________________________________________________   _________*/
+/* clr:    \_/ <--low-pulse does start the write-readback         \_/<--start */
+/*	          sequence with partital reset of internal 	  new sequence*/
+/*	          registers but the CONF-REG. 	  		              */
+/*        ____________________________________________________________________*/
+/* rst: _/       _   _   _        _   _   _   __       __   __   _            */
+/* stb: STB#1->_/1\_/2\_/3\_...._/7\_/8\_/9\_/10\_..../23\_/24\_/<-STB#25 edge*/
+/*						                   is needed  */
+/*	  						           to ACK     */
+/*             (D0 - D7 stb rising latch)                                     */
+/* data_in:     D0  D1  D2  .... D6  D7  don't ........ care(DC)              */
+/* data_out:    don't ...........care(DC)  D8   D9 ....  D22  D23   	      */
+/*					  (D8 - D23 stb falling latch) 	      */
+/* flag_Read:				  _________...._________              */
+/*      __DC_   ____________...._________/                      \_            */
+/* counter:								      */
+/*    [00]DC[00][01][02] ............[08][9][10]............[24][00]	      */
+/* CONF-REG:					                              */
+/*    [00] [				CONF-REG               ]              */
+/* wreg:   							              */
+/*    [00]DC[00][  wreg=SHFT(wreg)  ][ADR][DATA][wreg=SHFT(wreg]	      */
+/* wreg2:		  						      */
+/*    					  [	   (COPY)=ADDR ]	      */
+static int upboard_fpga_read(void *context, unsigned int reg, unsigned int *val)
+{
+	struct upboard_fpga * const fpga = context;
+	int i;
+
+	gpiod_set_value(fpga->clear_gpio, 0);
+	gpiod_set_value(fpga->clear_gpio, 1);
+
+	reg |= UPFPGA_READ_FLAG;
+
+	for (i = UPFPGA_ADDRESS_SIZE; i >= 0; i--) {
+		gpiod_set_value(fpga->strobe_gpio, 0);
+		gpiod_set_value(fpga->datain_gpio, (reg >> i) & 0x1);
+		gpiod_set_value(fpga->strobe_gpio, 1);
+	}
+
+	gpiod_set_value(fpga->strobe_gpio, 0);
+	*val = 0;
+
+	for (i = UPFPGA_REGISTER_SIZE - 1; i >= 0; i--) {
+		gpiod_set_value(fpga->strobe_gpio, 1);
+		gpiod_set_value(fpga->strobe_gpio, 0);
+		*val |= gpiod_get_value(fpga->dataout_gpio) << i;
+	}
+
+	gpiod_set_value(fpga->strobe_gpio, 1);
+
+	return 0;
+}
+
+/* Write Sequence:                                                   	      */
+/*      ___   ____________________________________________________   _________*/
+/* clr:    \_/ <--low-pulse does start the write-readback         \_/<--start */
+/*	          sequence with partital reset of internal 	  new sequence*/
+/*	          registers but the CONF-REG. 	  			      */
+/*        ____________________________________________________________________*/
+/* rst: _/       _   _   _        _   _   _   __       __   __   _            */
+/* stb: STB#1->_/1\_/2\_/3\_...._/7\_/8\_/9\_/10\_..../23\_/24\_/<-STB#25 edge*/
+/*						                   is needed  */
+/*								   to ACK     */
+/*             (D0 - D23 stb rising latch)                                    */
+/* data_in:     D0  D1  D2  .... D6  D7  D8  D9 ....  D22  D23                */
+/* data_out:    don't ................................care (DC)               */
+/* flag_Read:			 					      */
+/*      __DC_   ____________....__________________________________            */
+/* counter:								      */
+/*    [00]DC[00][01][02] ............[08][9][10]............[24][00]          */
+/* wreg:								      */
+/*    [00]DC[00][wreg=SHFT(wreg)&dat_in ][SHFT(wreg)&dat_in][DAT]             */
+/* wreg2:							              */
+/*    					 [     (COPY)=ADDR     ]	      */
+/* CONF-REG:								      */
+/*    [00] [	     CONF-REG = OLD VALUE		       ][CONF-REG=DAT]*/
+static int upboard_fpga_write(void *context, unsigned int reg, unsigned int val)
+{
+	struct upboard_fpga * const fpga = context;
+	int i;
+
+	gpiod_set_value(fpga->clear_gpio, 0);
+	gpiod_set_value(fpga->clear_gpio, 1);
+
+	for (i = UPFPGA_ADDRESS_SIZE; i >= 0; i--) {
+		gpiod_set_value(fpga->strobe_gpio, 0);
+		gpiod_set_value(fpga->datain_gpio, (reg >> i) & 0x1);
+		gpiod_set_value(fpga->strobe_gpio, 1);
+	}
+
+	gpiod_set_value(fpga->strobe_gpio, 0);
+
+	for (i = UPFPGA_REGISTER_SIZE - 1; i >= 0; i--) {
+		gpiod_set_value(fpga->datain_gpio, (val >> i) & 0x1);
+		gpiod_set_value(fpga->strobe_gpio, 1);
+		gpiod_set_value(fpga->strobe_gpio, 0);
+	}
+
+	gpiod_set_value(fpga->strobe_gpio, 1);
+
+	return 0;
+}
+
+static const struct regmap_range upboard_up_readable_ranges[] = {
+	regmap_reg_range(UPFPGA_REG_PLATFORM_ID, UPFPGA_REG_FIRMWARE_ID),
+	regmap_reg_range(UPFPGA_REG_FUNC_EN0, UPFPGA_REG_FUNC_EN0),
+	regmap_reg_range(UPFPGA_REG_GPIO_DIR0, UPFPGA_REG_GPIO_DIR1),
+};
+
+static const struct regmap_range upboard_up_writable_ranges[] = {
+	regmap_reg_range(UPFPGA_REG_FUNC_EN0, UPFPGA_REG_FUNC_EN0),
+	regmap_reg_range(UPFPGA_REG_GPIO_DIR0, UPFPGA_REG_GPIO_DIR1),
+};
+
+static const struct regmap_access_table upboard_up_readable_table = {
+	.yes_ranges = upboard_up_readable_ranges,
+	.n_yes_ranges = ARRAY_SIZE(upboard_up_readable_ranges),
+};
+
+static const struct regmap_access_table upboard_up_writable_table = {
+	.yes_ranges = upboard_up_writable_ranges,
+	.n_yes_ranges = ARRAY_SIZE(upboard_up_writable_ranges),
+};
+
+static const struct regmap_config upboard_up_regmap_config = {
+	.reg_bits = UPFPGA_ADDRESS_SIZE,
+	.val_bits = UPFPGA_REGISTER_SIZE,
+	.max_register = UPFPGA_REG_MAX,
+	.reg_read = upboard_fpga_read,
+	.reg_write = upboard_fpga_write,
+	.fast_io = false,
+	.cache_type = REGCACHE_RBTREE,
+	.rd_table = &upboard_up_readable_table,
+	.wr_table = &upboard_up_writable_table,
+};
+
+static struct upboard_led_data upboard_up_led_data[] = {
+	{ .bit = 0, .colour = "yellow" },
+	{ .bit = 1, .colour = "green" },
+	{ .bit = 2, .colour = "red" },
+};
+
+static const struct mfd_cell upboard_up_mfd_cells[] = {
+	{ .name = "upboard-pinctrl" },
+	MFD_CELL_BASIC("upboard-led", NULL, &upboard_up_led_data[0],
+		       sizeof(*upboard_up_led_data), 0),
+	MFD_CELL_BASIC("upboard-led", NULL, &upboard_up_led_data[1],
+		       sizeof(*upboard_up_led_data), 1),
+	MFD_CELL_BASIC("upboard-led", NULL, &upboard_up_led_data[2],
+		       sizeof(*upboard_up_led_data), 2),
+};
+
+/* UP Squared 6000 EHL board */
+
+static const struct upboard_fpga_data upboard_up_fpga_data = {
+	.regmapconf = &upboard_up_regmap_config,
+	.cells = upboard_up_mfd_cells,
+	.ncells = ARRAY_SIZE(upboard_up_mfd_cells),
+};
+
+static const struct mfd_cell upboard_pinctrl_cells[] = {
+	{ .name = "upboard-pinctrl" },
+};
+
+static const struct upboard_fpga_data upboard_pinctrl_data = {
+	.regmapconf = &upboard_up_regmap_config,
+	.cells = upboard_pinctrl_cells,
+	.ncells = ARRAY_SIZE(upboard_pinctrl_cells),
+};
+
+/* UP^2 board */
+
+static const struct regmap_range upboard_up2_readable_ranges[] = {
+	regmap_reg_range(UPFPGA_REG_PLATFORM_ID, UPFPGA_REG_FIRMWARE_ID),
+	regmap_reg_range(UPFPGA_REG_FUNC_EN0, UPFPGA_REG_FUNC_EN1),
+	regmap_reg_range(UPFPGA_REG_GPIO_EN0, UPFPGA_REG_GPIO_EN2),
+	regmap_reg_range(UPFPGA_REG_GPIO_DIR0, UPFPGA_REG_GPIO_DIR2),
+};
+
+static const struct regmap_range upboard_up2_writable_ranges[] = {
+	regmap_reg_range(UPFPGA_REG_FUNC_EN0, UPFPGA_REG_FUNC_EN1),
+	regmap_reg_range(UPFPGA_REG_GPIO_EN0, UPFPGA_REG_GPIO_EN2),
+	regmap_reg_range(UPFPGA_REG_GPIO_DIR0, UPFPGA_REG_GPIO_DIR2),
+};
+
+static const struct regmap_access_table upboard_up2_readable_table = {
+	.yes_ranges = upboard_up2_readable_ranges,
+	.n_yes_ranges = ARRAY_SIZE(upboard_up2_readable_ranges),
+};
+
+static const struct regmap_access_table upboard_up2_writable_table = {
+	.yes_ranges = upboard_up2_writable_ranges,
+	.n_yes_ranges = ARRAY_SIZE(upboard_up2_writable_ranges),
+};
+
+static const struct regmap_config upboard_up2_regmap_config = {
+	.reg_bits = UPFPGA_ADDRESS_SIZE,
+	.val_bits = UPFPGA_REGISTER_SIZE,
+	.max_register = UPFPGA_REG_MAX,
+	.reg_read = upboard_fpga_read,
+	.reg_write = upboard_fpga_write,
+	.fast_io = false,
+	.cache_type = REGCACHE_NONE,
+	.rd_table = &upboard_up2_readable_table,
+	.wr_table = &upboard_up2_writable_table,
+};
+
+static struct upboard_led_data upboard_up2_led_data[] = {
+	{ .bit = 0, .colour = "blue" },
+	{ .bit = 1, .colour = "yellow" },
+	{ .bit = 2, .colour = "green" },
+	{ .bit = 3, .colour = "red" },
+};
+
+static const struct mfd_cell upboard_up2_mfd_cells[] = {
+	{ .name = "upboard-pinctrl" },
+	MFD_CELL_BASIC("upboard-led", NULL, &upboard_up2_led_data[0],
+		       sizeof(*upboard_up2_led_data), 0),
+	MFD_CELL_BASIC("upboard-led", NULL, &upboard_up2_led_data[0],
+		       sizeof(*upboard_up2_led_data), 1),
+	MFD_CELL_BASIC("upboard-led", NULL, &upboard_up2_led_data[0],
+		       sizeof(*upboard_up2_led_data), 2),
+	MFD_CELL_BASIC("upboard-led", NULL, &upboard_up2_led_data[0],
+		       sizeof(*upboard_up2_led_data), 3),
+};
+
+static const struct upboard_fpga_data upboard_up2_fpga_data = {
+	.regmapconf = &upboard_up2_regmap_config,
+	.cells = upboard_up2_mfd_cells,
+	.ncells = ARRAY_SIZE(upboard_up2_mfd_cells),
+};
+
+/* UP-CRST02 carrier board for UP Core */
+
+/* same MAX10 config as UP2, but same LED cells as UP1 */
+static const struct upboard_fpga_data upboard_upcore_crst02_fpga_data = {
+	.regmapconf = &upboard_up2_regmap_config,
+	.cells = upboard_up_mfd_cells,
+	.ncells = ARRAY_SIZE(upboard_up_mfd_cells),
+};
+
+static struct gpio_led upboard_gpio_leds[] = {
+	{
+		.name = "upboard:blue:",
+		.gpio = APL_GPIO_218,
+		.default_state = LEDS_GPIO_DEFSTATE_KEEP,
+	},
+};
+
+static struct gpio_led_platform_data upboard_gpio_led_platform_data = {
+	.num_leds = ARRAY_SIZE(upboard_gpio_leds),
+	.leds = upboard_gpio_leds,
+};
+
+static const struct mfd_cell upboard_gpio_led_cells[] = {
+	MFD_CELL_BASIC("leds-gpio", NULL, &upboard_gpio_led_platform_data,
+		       sizeof(upboard_gpio_led_platform_data), 0)
+};
+
+static int __init upboard_fpga_gpio_init(struct upboard_fpga *fpga)
+{
+	enum gpiod_flags flags;
+
+	flags = fpga->uninitialised ? GPIOD_OUT_LOW : GPIOD_ASIS;
+
+	fpga->enable_gpio = devm_gpiod_get(fpga->dev, "enable", flags);
+	if (IS_ERR(fpga->enable_gpio))
+		return PTR_ERR(fpga->enable_gpio);
+
+	fpga->clear_gpio = devm_gpiod_get(fpga->dev, "clear", GPIOD_OUT_LOW);
+	if (IS_ERR(fpga->clear_gpio))
+		return PTR_ERR(fpga->clear_gpio);
+
+	fpga->strobe_gpio = devm_gpiod_get(fpga->dev, "strobe", GPIOD_OUT_LOW);
+	if (IS_ERR(fpga->strobe_gpio))
+		return PTR_ERR(fpga->strobe_gpio);
+
+	fpga->datain_gpio = devm_gpiod_get(fpga->dev, "datain", GPIOD_OUT_LOW);
+	if (IS_ERR(fpga->datain_gpio))
+		return PTR_ERR(fpga->datain_gpio);
+
+	fpga->dataout_gpio = devm_gpiod_get(fpga->dev, "dataout", GPIOD_IN);
+	if (IS_ERR(fpga->dataout_gpio))
+		return PTR_ERR(fpga->dataout_gpio);
+
+	/*
+	 * The SoC pinctrl driver may not support reserving the GPIO line for
+	 * FPGA reset without causing an undesired reset pulse. This will clear
+	 * any settings on the FPGA, so only do it if we must.
+	 * Reset gpio defaults HIGH, get gpio and set to LOW, then set back to
+	 * HIGH as a pulse.
+	 */
+	if (fpga->uninitialised) {
+		fpga->reset_gpio = devm_gpiod_get(fpga->dev, "reset", GPIOD_OUT_LOW);
+		if (IS_ERR(fpga->reset_gpio))
+			return PTR_ERR(fpga->reset_gpio);
+
+		gpiod_set_value(fpga->reset_gpio, 1);
+	}
+
+	gpiod_set_value(fpga->enable_gpio, 1);
+	fpga->uninitialised = false;
+
+	return 0;
+}
+
+/*
+ * This function is for debugging with user for showing firmware information.
+ */
+static int __init upboard_fpga_show_firmware_info(struct upboard_fpga *fpga)
+{
+	unsigned int platform_id, manufacturer_id;
+	unsigned int firmware_id, build, major, minor, patch;
+	int ret;
+
+	if (!fpga)
+		return -ENOMEM;
+
+	ret = regmap_read(fpga->regmap, UPFPGA_REG_PLATFORM_ID, &platform_id);
+	if (ret)
+		return ret;
+
+	manufacturer_id = platform_id & MENUFACTURER_ID_MASK;
+	if (manufacturer_id != AAEON_MANUFACTURER_ID) {
+		dev_err(fpga->dev,
+			"driver not compatible with custom FPGA FW from manufacturer id 0x%02x. Exiting",
+			manufacturer_id);
+		return -ENODEV;
+	}
+
+	ret = regmap_read(fpga->regmap, UPFPGA_REG_FIRMWARE_ID, &firmware_id);
+	if (ret)
+		return ret;
+
+	build = (firmware_id >> FIRMWARE_ID_BUILD_OFFSET) & FIRMWARE_ID_MASK;
+	major = (firmware_id >> FIRMWARE_ID_MAJOR_OFFSET) & FIRMWARE_ID_MASK;
+	minor = (firmware_id >> FIRMWARE_ID_MINOR_OFFSET) & FIRMWARE_ID_MASK;
+	patch = (firmware_id >> FIRMWARE_ID_PATCH_OFFSET) & FIRMWARE_ID_MASK;
+
+	if (major != SUPPORTED_FW_MAJOR) {
+		dev_err(fpga->dev, "unsupported FPGA FW v%u.%u.%u build 0x%02x",
+			major, minor, patch, build);
+
+		return -ENODEV;
+	}
+
+	dev_info(fpga->dev, "compatible FPGA FW v%u.%u.%u build 0x%02x",
+		 major, minor, patch, build);
+
+	return 0;
+}
+
+/*
+ * MFD upboard-fpga is acpi driver and can recognize the AANT ID from different
+ * kind of upboards. We get the led gpio initialized information from this
+ * then add led-upboard driver.
+ */
+static void upboard_led_gpio_init(struct upboard_fpga *fpga)
+{
+	struct gpio_led blue_led, yellow_led, green_led, red_led;
+	struct gpio_desc *desc;
+	int blue_gpio = -1, yellow_gpio = -1, green_gpio = -1, red_gpio = -1;
+	int leds = 0;
+	static struct gpio_led upboard_gpio_leds[8];
+	static struct gpio_led_platform_data upboard_gpio_led_platform_data;
+	static const struct mfd_cell upboard_gpio_led_cells[] = {
+		MFD_CELL_BASIC("leds-gpio", NULL,
+			       &upboard_gpio_led_platform_data,
+			       sizeof(upboard_gpio_led_platform_data), 0)
+	};
+
+	desc = devm_gpiod_get(fpga->dev, "blue", GPIOD_OUT_LOW);
+	if (!IS_ERR(desc)) {
+		blue_gpio = desc_to_gpio(desc);
+		leds++;
+		devm_gpiod_put(fpga->dev, desc);
+	}
+	desc = devm_gpiod_get(fpga->dev, "yellow", GPIOD_OUT_LOW);
+	if (!IS_ERR(desc)) {
+		yellow_gpio = desc_to_gpio(desc);
+		leds++;
+		devm_gpiod_put(fpga->dev, desc);
+	}
+	desc = devm_gpiod_get(fpga->dev, "green", GPIOD_OUT_LOW);
+	if (!IS_ERR(desc)) {
+		green_gpio = desc_to_gpio(desc);
+		leds++;
+		devm_gpiod_put(fpga->dev, desc);
+	}
+	desc = devm_gpiod_get(fpga->dev, "red", GPIOD_OUT_LOW);
+	if (!IS_ERR(desc)) {
+		red_gpio = desc_to_gpio(desc);
+		leds++;
+		devm_gpiod_put(fpga->dev, desc);
+	}
+
+	/* no leds */
+	if (leds == 0)
+		return;
+
+	leds = 0;
+	if (blue_gpio > -1) {
+		blue_led.name = "upboard:blue:";
+		blue_led.gpio = blue_gpio;
+		blue_led.default_state = LEDS_GPIO_DEFSTATE_KEEP;
+		upboard_gpio_leds[leds++] = blue_led;
+	}
+	if (yellow_gpio > -1) {
+		yellow_led.name = "upboard:yellow:";
+		yellow_led.gpio = yellow_gpio;
+		yellow_led.default_state = LEDS_GPIO_DEFSTATE_KEEP;
+		upboard_gpio_leds[leds++] = yellow_led;
+	}
+	if (green_gpio > -1) {
+		green_led.name = "upboard:green:";
+		green_led.gpio = green_gpio;
+		green_led.default_state = LEDS_GPIO_DEFSTATE_KEEP;
+		upboard_gpio_leds[leds++] = green_led;
+	}
+	if (red_gpio > -1) {
+		red_led.name = "upboard:red:";
+		red_led.gpio = red_gpio;
+		red_led.default_state = LEDS_GPIO_DEFSTATE_KEEP;
+		upboard_gpio_leds[leds++] = red_led;
+	}
+
+	upboard_gpio_led_platform_data.num_leds = leds;
+	upboard_gpio_led_platform_data.leds = upboard_gpio_leds;
+/*
+ *	Refer https://www.kernel.org/doc/htmldocs/writing_musb_glue_layer/device-platform-data.html,
+ *	the id field could be set to -1 (equivalent to PLATFORM_DEVID_NONE),
+ *  -2 (equivalent to PLATFORM_DEVID_AUTO) or start with 0 for the first
+ *	device of this kind if we want a specific id number.
+ */
+	if (devm_mfd_add_devices(fpga->dev, 0,
+				 upboard_gpio_led_cells,
+				 ARRAY_SIZE(upboard_gpio_led_cells),
+				 NULL, 0, NULL)) {
+		dev_info(fpga->dev, "Failed to add GPIO leds");
+	}
+}
+
+static const struct acpi_device_id upboard_fpga_acpi_match[] = {
+	{ "AANT0000", (kernel_ulong_t)&upboard_pinctrl_data },
+	{ "AANT0F00", (kernel_ulong_t)&upboard_up_fpga_data },
+	{ "AANT0F01", (kernel_ulong_t)&upboard_up2_fpga_data },
+	{ "AANT0F02", (kernel_ulong_t)&upboard_up_fpga_data },
+	{ "AANT0F03", (kernel_ulong_t)&upboard_upcore_crst02_fpga_data },
+	{ "AANT0F04", (kernel_ulong_t)&upboard_up_fpga_data },
+	{ }
+};
+MODULE_DEVICE_TABLE(acpi, upboard_fpga_acpi_match);
+
+static const struct dmi_system_id upboard_dmi_table[] __initconst = {
+	{
+		.matches = { /* UP */
+			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "AAEON"),
+			DMI_EXACT_MATCH(DMI_BOARD_NAME, "UP-CHT01"),
+			DMI_EXACT_MATCH(DMI_BOARD_VERSION, "V0.4"),
+		},
+		.driver_data = (void *)UPFPGA_QUIRK_UNINITIALISED,
+	},
+	{
+		.matches = { /* UP2 */
+			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "AAEON"),
+			DMI_EXACT_MATCH(DMI_BOARD_NAME, "UP-APL01"),
+			DMI_EXACT_MATCH(DMI_BOARD_VERSION, "V0.3"),
+		},
+		.driver_data = (void *)(UPFPGA_QUIRK_UNINITIALISED |
+			UPFPGA_QUIRK_HRV1_IS_PROTO2),
+	},
+	{
+		.matches = { /* UP2 Pro*/
+			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "AAEON"),
+			DMI_EXACT_MATCH(DMI_BOARD_NAME, "UPN-APL01"),
+			DMI_EXACT_MATCH(DMI_BOARD_VERSION, "V1.0"),
+		},
+		.driver_data = (void *)UPFPGA_QUIRK_HRV1_IS_PROTO2,
+	},
+	{
+		.matches = { /* UP2 */
+			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "AAEON"),
+			DMI_EXACT_MATCH(DMI_BOARD_NAME, "UP-APL01"),
+			DMI_EXACT_MATCH(DMI_BOARD_VERSION, "V0.4"),
+		},
+		.driver_data = (void *)UPFPGA_QUIRK_HRV1_IS_PROTO2,
+	},
+	{
+		.matches = { /* UP APL03 */
+			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "AAEON"),
+			DMI_EXACT_MATCH(DMI_BOARD_NAME, "UP-APL03"),
+			DMI_EXACT_MATCH(DMI_BOARD_VERSION, "V1.0"),
+		},
+		.driver_data = (void *)(UPFPGA_QUIRK_HRV1_IS_PROTO2 |
+			UPFPGA_QUIRK_GPIO_LED),
+	},
+	{
+		.matches = { /* UP Xtreme */
+			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "AAEON"),
+			DMI_EXACT_MATCH(DMI_BOARD_NAME, "UP-WHL01"),
+			DMI_EXACT_MATCH(DMI_BOARD_VERSION, "V0.1"),
+		},
+	},
+	{
+		.matches = { /* UP Xtreme i11 */
+			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "AAEON"),
+			DMI_EXACT_MATCH(DMI_BOARD_NAME, "UPX-TGL01"),
+		},
+	},
+	{
+		.matches = { /* UP Xtreme i12 */
+			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "AAEON"),
+			DMI_EXACT_MATCH(DMI_BOARD_NAME, "UPX-ADLP01"),
+		},
+	},
+	{
+		.matches = { /* UP Squared 6000*/
+			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "AAEON"),
+			DMI_EXACT_MATCH(DMI_BOARD_NAME, "UPN-EHL01"),
+		},
+	},
+	{
+		.matches = { /* UPS 6000 */
+			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "AAEON"),
+			DMI_EXACT_MATCH(DMI_BOARD_NAME, "UPS-EHL01"),
+		},
+	},
+	{ }
+};
+
+static int __init upboard_fpga_probe(struct platform_device *pdev)
+{
+	struct upboard_fpga *fpga;
+	const struct acpi_device_id *id;
+	const struct upboard_fpga_data *fpga_data;
+	const struct dmi_system_id *system_id;
+	unsigned long long hrv;
+	unsigned long quirks = 0;
+	int ret;
+
+	id = acpi_match_device(upboard_fpga_acpi_match, &pdev->dev);
+	if (!id)
+		return -ENODEV;
+
+	/* get fpga/EC protocol hardware version */
+	acpi_evaluate_integer(ACPI_HANDLE(&pdev->dev), "_HRV", NULL, &hrv);
+
+	system_id = dmi_first_match(upboard_dmi_table);
+	if (system_id)
+		quirks = (unsigned long)system_id->driver_data;
+
+	if (hrv == UPFPGA_PROTOCOL_V1_HRV &&
+	    (quirks & UPFPGA_QUIRK_HRV1_IS_PROTO2))
+		hrv = UPFPGA_PROTOCOL_V2_HRV;
+
+	fpga_data = (const struct upboard_fpga_data *) id->driver_data;
+
+	fpga = devm_kzalloc(&pdev->dev, sizeof(*fpga), GFP_KERNEL);
+	if (!fpga)
+		return -ENOMEM;
+
+	if (quirks & UPFPGA_QUIRK_UNINITIALISED) {
+		dev_info(&pdev->dev, "FPGA not initialised by this BIOS");
+		fpga->uninitialised = true;
+	}
+
+	dev_set_drvdata(&pdev->dev, fpga);
+	fpga->dev = &pdev->dev;
+	fpga->regmap = devm_regmap_init(&pdev->dev, NULL,
+					fpga, fpga_data->regmapconf);
+	fpga->regmapconf = fpga_data->regmapconf;
+
+	if (IS_ERR(fpga->regmap))
+		return PTR_ERR(fpga->regmap);
+
+	ret = upboard_fpga_gpio_init(fpga);
+	if (ret) {
+		/*
+		 * This is for compatiable with some upboards w/o FPGA firmware,
+		 * so just showing debug info and do not return directly.
+		 */
+		dev_info(&pdev->dev,
+			"failed to initialize FPGA common GPIOs: %d", ret);
+	} else {
+		upboard_fpga_show_firmware_info(fpga);
+	}
+
+	/* gpio leds initialize */
+	upboard_led_gpio_init(fpga);
+
+	if (quirks & UPFPGA_QUIRK_GPIO_LED) {
+		ret =  devm_mfd_add_devices(&pdev->dev, 0,
+					    upboard_gpio_led_cells,
+					    ARRAY_SIZE(upboard_gpio_led_cells),
+					    NULL, 0, NULL);
+		if (ret) {
+			dev_err(&pdev->dev, "Failed to add GPIO leds");
+			return ret;
+		}
+	}
+	return devm_mfd_add_devices(&pdev->dev, 0,
+				    fpga_data->cells,
+				    fpga_data->ncells,
+				    NULL, 0, NULL);
+}
+
+static struct platform_driver upboard_fpga_driver = {
+	.driver = {
+		.name = "upboard-fpga",
+		.acpi_match_table = upboard_fpga_acpi_match,
+	},
+};
+module_platform_driver_probe(upboard_fpga_driver, upboard_fpga_probe);
+
+MODULE_AUTHOR("Gary Wang <garywang@aaeon.com.tw>");
+MODULE_AUTHOR("Javier Arteaga <javier@emutex.com>");
+MODULE_DESCRIPTION("UP Board FPGA driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/mfd/upboard-fpga.h b/include/linux/mfd/upboard-fpga.h
new file mode 100644
index 000000000000..c8ca807528be
--- /dev/null
+++ b/include/linux/mfd/upboard-fpga.h
@@ -0,0 +1,58 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Intel AAEON UP Board series platform core driver
+ * and FPGA configuration support
+ *
+ * Copyright (c) 2017, Emutex Ltd. All rights reserved.
+ * Copyright (c) 2022, YunJingTech Ltd.
+ *
+ * Author: Javier Arteaga <javier@emutex.com>
+ */
+
+#ifndef __LINUX_MFD_UPBOARD_FPGA_H
+#define __LINUX_MFD_UPBOARD_FPGA_H
+
+/* fpga/EC protocol hardware version */
+#define UPFPGA_PROTOCOL_V1_HRV		1
+#define UPFPGA_PROTOCOL_V2_HRV		2
+
+#define UPFPGA_ADDRESS_SIZE		7
+#define UPFPGA_REGISTER_SIZE		16
+
+#define UPFPGA_READ_FLAG		(1 << UPFPGA_ADDRESS_SIZE)
+
+enum upboard_fpgareg {
+	UPFPGA_REG_PLATFORM_ID		= 0x10,
+	UPFPGA_REG_FIRMWARE_ID		= 0x11,
+	UPFPGA_REG_FUNC_EN0		= 0x20,
+	UPFPGA_REG_FUNC_EN1		= 0x21,
+	UPFPGA_REG_GPIO_EN0		= 0x30,
+	UPFPGA_REG_GPIO_EN1		= 0x31,
+	UPFPGA_REG_GPIO_EN2		= 0x32,
+	UPFPGA_REG_GPIO_DIR0		= 0x40,
+	UPFPGA_REG_GPIO_DIR1		= 0x41,
+	UPFPGA_REG_GPIO_DIR2		= 0x42,
+	UPFPGA_REG_MAX,
+};
+
+struct upboard_fpga {
+	struct device			*dev;
+	struct regmap			*regmap;
+	const struct regmap_config	*regmapconf;
+	struct gpio_desc		*enable_gpio;
+	struct gpio_desc		*reset_gpio;
+	struct gpio_desc		*clear_gpio;
+	struct gpio_desc		*strobe_gpio;
+	struct gpio_desc		*datain_gpio;
+	struct gpio_desc		*dataout_gpio;
+	bool				uninitialised;
+};
+
+struct upboard_led_data {
+	unsigned int			bit;
+	const char			*colour;
+};
+
+bool regmap_check_writeable(struct upboard_fpga *fpga, unsigned int reg);
+
+#endif /*  __LINUX_MFD_UPBOARD_FPGA_H */
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [RFC 2/3] pinctrl: Add support pin control for UP board CPLD/FPGA
  2022-12-07 16:33 [RFC 0/3] Add support control UP board CPLD/FPGA pin control larry.lai
  2022-12-07 16:33 ` [RFC 1/3] mfd: Add support for UP board CPLD/FPGA larry.lai
@ 2022-12-07 16:33 ` larry.lai
  2022-12-07 21:32   ` Andy Shevchenko
  2022-12-07 16:33 ` [RFC 3/3] leds: Add support for UP board CPLD onboard LEDS larry.lai
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 12+ messages in thread
From: larry.lai @ 2022-12-07 16:33 UTC (permalink / raw)
  To: lee, andriy.shevchenko, linus.walleij, pavel
  Cc: linux-kernel, linux-gpio, linux-leds, GaryWang, musa.lin,
	jack.chang, noah.hung, larry.lai

The UP Squared board <http://www.upboard.com> implements certain
features (pin control) through an on-board FPGA.

Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: larry.lai <larry.lai@yunjingtech.com>
---
PATCH V3 -> RFC 2022/11/23:
(1) Refer 2022/11/09 Linus Walleij review, cleaned up coding style and
addressed review comments.

PATCH V2 -> V3:
There's no change.

PATCH V1 -> V2:
(1) Synchronized with upboard github to rc2
(2) Refer 2022/10/19 Mark Brown and Andy Shevchenko review, we removed
the regmap and acpi patches and implement in upboard pinctrl driver.
(3) Refer 2022/10/19 Andy Shevchenko review, fixed the coding style
issues, removed using gpio_xxxx API and removed including <linux/gpio.h>.
---
 drivers/pinctrl/Kconfig           |   14 +
 drivers/pinctrl/Makefile          |    1 +
 drivers/pinctrl/pinctrl-upboard.c | 1384 +++++++++++++++++++++++++++++
 3 files changed, 1399 insertions(+)
 create mode 100644 drivers/pinctrl/pinctrl-upboard.c

diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 1cf74b0c42e5..cc8dae75289b 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -483,6 +483,20 @@ config PINCTRL_THUNDERBAY
 	  rate control and direction control. This module will be
 	  called as pinctrl-thunderbay.
 
+config PINCTRL_UPBOARD
+	tristate "UP board FPGA pin controller"
+	depends on (X86 && ACPI) || COMPILE_TEST
+	depends on MFD_INTEL_UPBOARD_FPGA
+	select GENERIC_PINCONF
+	select PINMUX
+	select PINCONF
+	help
+	  Pin controller for the FPGA GPIO lines on UP boards. Due to the
+	  hardware layout, these are meant to be controlled in tandem with their
+	  corresponding Intel SoC GPIOs.
+	  To compile this driver as a module, choose M here: the module
+	  will be called pinctrl-upboard.
+
 config PINCTRL_ZYNQ
 	bool "Pinctrl driver for Xilinx Zynq"
 	depends on ARCH_ZYNQ
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index e76f5cdc64b0..c366706d36e7 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -48,6 +48,7 @@ obj-$(CONFIG_PINCTRL_STMFX) 	+= pinctrl-stmfx.o
 obj-$(CONFIG_PINCTRL_SX150X)	+= pinctrl-sx150x.o
 obj-$(CONFIG_PINCTRL_TB10X)	+= pinctrl-tb10x.o
 obj-$(CONFIG_PINCTRL_THUNDERBAY) += pinctrl-thunderbay.o
+obj-$(CONFIG_PINCTRL_UPBOARD)	+= pinctrl-upboard.o
 obj-$(CONFIG_PINCTRL_ZYNQMP)	+= pinctrl-zynqmp.o
 obj-$(CONFIG_PINCTRL_ZYNQ)	+= pinctrl-zynq.o
 
diff --git a/drivers/pinctrl/pinctrl-upboard.c b/drivers/pinctrl/pinctrl-upboard.c
new file mode 100644
index 000000000000..e06151f4c128
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-upboard.c
@@ -0,0 +1,1384 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * UP Board FPGA-based pin controller driver
+ *
+ * Copyright (c) 2017, Emutex Ltd. All rights reserved.
+ * Copyright (c) 2022, YunJingTech Ltd.
+ *
+ * Authors: Javier Arteaga <javier@emutex.com>
+ *          Dan O'Donovan <dan@emutex.com>
+ *          Nicola Lunghi <nicolal@emutex.com>
+ *          Gary Wang <garywang@aaeon.com.tw>
+ */
+
+#include <linux/acpi.h>
+#include <linux/dmi.h>
+#include <linux/gpio/consumer.h>
+#include <linux/gpio/driver.h>
+#include <linux/kernel.h>
+#include <linux/mfd/upboard-fpga.h>
+#include <linux/module.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/interrupt.h>
+#include <linux/string.h>
+
+#include "core.h"
+
+/* Offset from regs */
+#define REVID				0x000
+#define REVID_SHIFT			16
+#define REVID_MASK			GENMASK(31, 16)
+
+#define CAPLIST				0x004
+#define CAPLIST_ID_SHIFT		16
+#define CAPLIST_ID_MASK			GENMASK(23, 16)
+#define CAPLIST_ID_GPIO_HW_INFO		1
+#define CAPLIST_ID_PWM			2
+#define CAPLIST_ID_BLINK		3
+#define CAPLIST_ID_EXP			4
+#define CAPLIST_NEXT_SHIFT		0
+#define CAPLIST_NEXT_MASK		GENMASK(15, 0)
+
+#define PADBAR				0x00c
+
+#define PADOWN_BITS			4
+#define PADOWN_SHIFT(p)			((p) % 8 * PADOWN_BITS)
+#define PADOWN_MASK(p)			(GENMASK(3, 0) << PADOWN_SHIFT(p))
+#define PADOWN_GPP(p)			((p) / 8)
+
+/* Offset from pad_regs */
+#define PADCFG0				0x000
+#define PADCFG0_RXEVCFG_SHIFT		25
+#define PADCFG0_RXEVCFG_MASK		GENMASK(26, 25)
+#define PADCFG0_RXEVCFG_LEVEL		0
+#define PADCFG0_RXEVCFG_EDGE		1
+#define PADCFG0_RXEVCFG_DISABLED	2
+#define PADCFG0_RXEVCFG_EDGE_BOTH	3
+#define PADCFG0_PREGFRXSEL		BIT(24)
+#define PADCFG0_RXINV			BIT(23)
+#define PADCFG0_GPIROUTIOXAPIC		BIT(20)
+#define PADCFG0_GPIROUTSCI		BIT(19)
+#define PADCFG0_GPIROUTSMI		BIT(18)
+#define PADCFG0_GPIROUTNMI		BIT(17)
+#define PADCFG0_PMODE_SHIFT		10
+#define PADCFG0_PMODE_MASK		GENMASK(13, 10)
+#define PADCFG0_PMODE_GPIO		0
+#define PADCFG0_GPIORXDIS		BIT(9)
+#define PADCFG0_GPIOTXDIS		BIT(8)
+#define PADCFG0_GPIORXSTATE		BIT(1)
+#define PADCFG0_GPIOTXSTATE		BIT(0)
+
+#define PADCFG1				0x004
+#define PADCFG1_TERM_UP			BIT(13)
+#define PADCFG1_TERM_SHIFT		10
+#define PADCFG1_TERM_MASK		GENMASK(12, 10)
+#define PADCFG1_TERM_20K		BIT(2)
+#define PADCFG1_TERM_5K			BIT(1)
+#define PADCFG1_TERM_1K			BIT(0)
+#define PADCFG1_TERM_833		(BIT(1) | BIT(0))
+
+#define PADCFG2				0x008
+#define PADCFG2_DEBEN			BIT(0)
+#define PADCFG2_DEBOUNCE_SHIFT		1
+#define PADCFG2_DEBOUNCE_MASK		GENMASK(4, 1)
+
+#define DEBOUNCE_PERIOD_NSEC		31250
+
+/* Additional features supported by the hardware */
+#define PINCTRL_FEATURE_DEBOUNCE	BIT(0)
+#define PINCTRL_FEATURE_1K_PD		BIT(1)
+
+#define pin_to_padno(c, p)	((p) - (c)->pin_base)
+#define padgroup_offset(g, p)	((p) - (g)->base)
+
+struct intel_pinctrl {
+	struct device *dev;
+	raw_spinlock_t lock;
+	struct pinctrl_desc pctldesc;
+	struct pinctrl_dev *pctldev;
+	struct gpio_chip chip;
+	struct irq_chip irqchip;
+	void *soc;
+	void *communities;
+	size_t ncommunities;
+};
+
+struct upboard_pin {
+	struct regmap_field *funcbit;
+	struct regmap_field *enbit;
+	struct regmap_field *dirbit;
+	/* native pin controllers: number, base, irq */
+	unsigned int gpio;
+	unsigned int base;
+	int irq;
+	void __iomem *regs;
+};
+
+struct upboard_pinctrl {
+	struct gpio_chip chip;
+	struct device *dev;
+	struct pinctrl_dev *pctldev;
+	struct pinctrl_desc *pctldesc;
+	struct upboard_pin *pins;
+	struct regmap *regmap;
+	const unsigned int *rpi_mapping;
+	int ident;
+};
+
+enum upboard_func0_fpgabit {
+	UPFPGA_I2C0_EN = 8,
+	UPFPGA_I2C1_EN = 9,
+	UPFPGA_CEC0_EN = 12,
+	UPFPGA_ADC0_EN = 14,
+};
+
+static const struct reg_field upboard_i2c0_reg =
+	REG_FIELD(UPFPGA_REG_FUNC_EN0, UPFPGA_I2C0_EN, UPFPGA_I2C0_EN);
+
+static const struct reg_field upboard_i2c1_reg =
+	REG_FIELD(UPFPGA_REG_FUNC_EN0, UPFPGA_I2C1_EN, UPFPGA_I2C1_EN);
+
+static const struct reg_field upboard_adc0_reg =
+	REG_FIELD(UPFPGA_REG_FUNC_EN0, UPFPGA_ADC0_EN, UPFPGA_ADC0_EN);
+
+/* Pin group information */
+struct upboard_pingroup {
+	const char *name;
+	const unsigned int *pins;
+	size_t npins;
+};
+
+/* Pin function information */
+struct upboard_function {
+	const char *name;
+	const char * const *groups;
+	size_t ngroups;
+};
+
+#define UPBOARD_BIT_TO_PIN(r, bit) \
+	((r) * UPFPGA_REGISTER_SIZE + (bit))
+
+/*
+ * UP board data
+ */
+
+#define UPBOARD_UP_BIT_TO_PIN(r, id) (UPBOARD_BIT_TO_PIN(r, UPFPGA_UP_##id))
+
+#define UPBOARD_UP_PIN_ANON(r, bit)					\
+	{								\
+		.number = UPBOARD_BIT_TO_PIN(r, bit),			\
+	}
+
+#define UPBOARD_UP_PIN_NAME(r, id)					\
+	{								\
+		.number = UPBOARD_UP_BIT_TO_PIN(r, id),			\
+		.name = #id,						\
+	}
+
+#define UPBOARD_UP_PIN_FUNC(r, id, data)				\
+	{								\
+		.number = UPBOARD_UP_BIT_TO_PIN(r, id),			\
+		.name = #id,						\
+		.drv_data = (void *)(data),				\
+	}
+
+#define PIN_GROUP(n, p)				\
+	{					\
+		.name = (n),			\
+		.pins = (p),			\
+		.npins = ARRAY_SIZE((p)),	\
+	}
+
+#define FUNCTION(n, g)				\
+	{					\
+		.name = (n),			\
+		.groups = (g),			\
+		.ngroups = ARRAY_SIZE((g)),	\
+	}
+
+enum upboard_up_reg1_fpgabit {
+	UPFPGA_UP_I2C1_SDA,
+	UPFPGA_UP_I2C1_SCL,
+	UPFPGA_UP_ADC0,
+	UPFPGA_UP_GPIO17,
+	UPFPGA_UP_GPIO27,
+	UPFPGA_UP_GPIO22,
+	UPFPGA_UP_SPI_MOSI,
+	UPFPGA_UP_SPI_MISO,
+	UPFPGA_UP_SPI_CLK,
+	UPFPGA_UP_I2C0_SDA,
+	UPFPGA_UP_GPIO5,
+	UPFPGA_UP_GPIO6,
+	UPFPGA_UP_PWM1,
+	UPFPGA_UP_I2S_FRM,
+	UPFPGA_UP_GPIO26,
+	UPFPGA_UP_UART1_TX,
+};
+
+enum upboard_up_reg2_fpgabit {
+	UPFPGA_UP_UART1_RX,
+	UPFPGA_UP_I2S_CLK,
+	UPFPGA_UP_GPIO23,
+	UPFPGA_UP_GPIO24,
+	UPFPGA_UP_GPIO25,
+	UPFPGA_UP_SPI_CS0,
+	UPFPGA_UP_SPI_CS1,
+	UPFPGA_UP_I2C0_SCL,
+	UPFPGA_UP_PWM0,
+	UPFPGA_UP_GPIO16,
+	UPFPGA_UP_I2S_DIN,
+	UPFPGA_UP_I2S_DOUT,
+};
+
+#define UPFPGA_UP_UART1_RTS UPFPGA_UP_GPIO17
+#define UPFPGA_UP_UART1_CTS UPFPGA_UP_GPIO16
+
+static struct pinctrl_pin_desc upboard_up_pins[] = {
+	UPBOARD_UP_PIN_FUNC(0, I2C1_SDA, &upboard_i2c1_reg),
+	UPBOARD_UP_PIN_FUNC(0, I2C1_SCL, &upboard_i2c1_reg),
+	UPBOARD_UP_PIN_FUNC(0, ADC0, &upboard_adc0_reg),
+	UPBOARD_UP_PIN_NAME(0, UART1_RTS),
+	UPBOARD_UP_PIN_NAME(0, GPIO27),
+	UPBOARD_UP_PIN_NAME(0, GPIO22),
+	UPBOARD_UP_PIN_NAME(0, SPI_MOSI),
+	UPBOARD_UP_PIN_NAME(0, SPI_MISO),
+	UPBOARD_UP_PIN_NAME(0, SPI_CLK),
+	UPBOARD_UP_PIN_FUNC(0, I2C0_SDA, &upboard_i2c0_reg),
+	UPBOARD_UP_PIN_NAME(0, GPIO5),
+	UPBOARD_UP_PIN_NAME(0, GPIO6),
+	UPBOARD_UP_PIN_NAME(0, PWM1),
+	UPBOARD_UP_PIN_NAME(0, I2S_FRM),
+	UPBOARD_UP_PIN_NAME(0, GPIO26),
+	UPBOARD_UP_PIN_NAME(0, UART1_TX),
+	/* register 1 */
+	UPBOARD_UP_PIN_NAME(1, UART1_RX),
+	UPBOARD_UP_PIN_NAME(1, I2S_CLK),
+	UPBOARD_UP_PIN_NAME(1, GPIO23),
+	UPBOARD_UP_PIN_NAME(1, GPIO24),
+	UPBOARD_UP_PIN_NAME(1, GPIO25),
+	UPBOARD_UP_PIN_NAME(1, SPI_CS0),
+	UPBOARD_UP_PIN_NAME(1, SPI_CS1),
+	UPBOARD_UP_PIN_FUNC(1, I2C0_SCL, &upboard_i2c0_reg),
+	UPBOARD_UP_PIN_NAME(1, PWM0),
+	UPBOARD_UP_PIN_NAME(1, UART1_CTS),
+	UPBOARD_UP_PIN_NAME(1, I2S_DIN),
+	UPBOARD_UP_PIN_NAME(1, I2S_DOUT),
+};
+
+static const unsigned int upboard_up_rpi_mapping[] = {
+	UPBOARD_UP_BIT_TO_PIN(0, I2C0_SDA),
+	UPBOARD_UP_BIT_TO_PIN(1, I2C0_SCL),
+	UPBOARD_UP_BIT_TO_PIN(0, I2C1_SDA),
+	UPBOARD_UP_BIT_TO_PIN(0, I2C1_SCL),
+	UPBOARD_UP_BIT_TO_PIN(0, ADC0),
+	UPBOARD_UP_BIT_TO_PIN(0, GPIO5),
+	UPBOARD_UP_BIT_TO_PIN(0, GPIO6),
+	UPBOARD_UP_BIT_TO_PIN(1, SPI_CS1),
+	UPBOARD_UP_BIT_TO_PIN(1, SPI_CS0),
+	UPBOARD_UP_BIT_TO_PIN(0, SPI_MISO),
+	UPBOARD_UP_BIT_TO_PIN(0, SPI_MOSI),
+	UPBOARD_UP_BIT_TO_PIN(0, SPI_CLK),
+	UPBOARD_UP_BIT_TO_PIN(1, PWM0),
+	UPBOARD_UP_BIT_TO_PIN(0, PWM1),
+	UPBOARD_UP_BIT_TO_PIN(0, UART1_TX),
+	UPBOARD_UP_BIT_TO_PIN(1, UART1_RX),
+	UPBOARD_UP_BIT_TO_PIN(1, GPIO16),
+	UPBOARD_UP_BIT_TO_PIN(0, GPIO17),
+	UPBOARD_UP_BIT_TO_PIN(1, I2S_CLK),
+	UPBOARD_UP_BIT_TO_PIN(0, I2S_FRM),
+	UPBOARD_UP_BIT_TO_PIN(1, I2S_DIN),
+	UPBOARD_UP_BIT_TO_PIN(1, I2S_DOUT),
+	UPBOARD_UP_BIT_TO_PIN(0, GPIO22),
+	UPBOARD_UP_BIT_TO_PIN(1, GPIO23),
+	UPBOARD_UP_BIT_TO_PIN(1, GPIO24),
+	UPBOARD_UP_BIT_TO_PIN(1, GPIO25),
+	UPBOARD_UP_BIT_TO_PIN(0, GPIO26),
+	UPBOARD_UP_BIT_TO_PIN(0, GPIO27),
+};
+
+static const unsigned int uart1_pins[] = { 14, 15, 16, 17 };
+static const unsigned int uart2_pins[] = { 25, 27 };
+static const unsigned int i2c0_pins[]  = { 0, 1 };
+static const unsigned int i2c1_pins[]  = { 2, 3 };
+static const unsigned int spi2_pins[]  = { 8, 9, 10, 11 };
+static const unsigned int i2s0_pins[]  = { 18, 19, 20, 21 };
+static const unsigned int pwm0_pins[]  = { 12 };
+static const unsigned int pwm1_pins[]  = { 13 };
+static const unsigned int adc0_pins[]  = { 4 };
+
+static const struct upboard_pingroup pin_groups[] = {
+	PIN_GROUP("uart1_grp", uart1_pins),
+	PIN_GROUP("uart2_grp", uart2_pins),
+	PIN_GROUP("i2c0_grp", i2c0_pins),
+	PIN_GROUP("i2c1_grp", i2c1_pins),
+	PIN_GROUP("spi2_grp", spi2_pins),
+	PIN_GROUP("i2s0_grp", i2s0_pins),
+	PIN_GROUP("pwm0_grp", pwm0_pins),
+	PIN_GROUP("pwm1_grp", pwm1_pins),
+	PIN_GROUP("adc0_grp", adc0_pins),
+};
+
+static const char * const uart1_groups[] = { "uart1_grp" };
+static const char * const uart2_groups[] = { "uart2_grp" };
+static const char * const i2c0_groups[]  = { "i2c0_grp" };
+static const char * const i2c1_groups[]  = { "i2c1_grp" };
+static const char * const spi2_groups[]  = { "spi2_grp" };
+static const char * const i2s0_groups[]  = { "i2s0_grp" };
+static const char * const pwm0_groups[]  = { "pwm0_grp" };
+static const char * const pwm1_groups[]  = { "pwm1_grp" };
+static const char * const adc0_groups[]  = { "adc0_grp" };
+
+static const struct upboard_function pin_functions[] = {
+	FUNCTION("uart1", uart1_groups),
+	FUNCTION("uart2", uart2_groups),
+	FUNCTION("i2c0",  i2c0_groups),
+	FUNCTION("i2c1",  i2c1_groups),
+	FUNCTION("spi2",  spi2_groups),
+	FUNCTION("i2s0",  i2s0_groups),
+	FUNCTION("pwm0",  pwm0_groups),
+	FUNCTION("pwm1",  pwm1_groups),
+	FUNCTION("adc0",  adc0_groups),
+};
+
+/*
+ * UP^2 board data
+ */
+
+#define UPBOARD_UP2_BIT_TO_PIN(r, id) (UPBOARD_BIT_TO_PIN(r, UPFPGA_UP2_##id))
+
+#define UPBOARD_UP2_PIN_ANON(r, bit)					\
+	{								\
+		.number = UPBOARD_BIT_TO_PIN(r, bit),			\
+		.name = "NONAME",					\
+	}
+
+#define UPBOARD_UP2_PIN_NAME(r, id)					\
+	{								\
+		.number = UPBOARD_UP2_BIT_TO_PIN(r, id),		\
+		.name = #id,						\
+	}
+
+#define UPBOARD_UP2_PIN_FUNC(r, id, data)				\
+	{								\
+		.number = UPBOARD_UP2_BIT_TO_PIN(r, id),		\
+		.name = #id,						\
+		.drv_data = (void *)(data),				\
+	}
+
+enum upboard_up2_reg0_fpgabit {
+	UPFPGA_UP2_UART1_TXD,
+	UPFPGA_UP2_UART1_RXD,
+	UPFPGA_UP2_UART1_RTS,
+	UPFPGA_UP2_UART1_CTS,
+	UPFPGA_UP2_GPIO3,
+	UPFPGA_UP2_GPIO5,
+	UPFPGA_UP2_GPIO6,
+	UPFPGA_UP2_GPIO11,
+	UPFPGA_UP2_EXHAT_LVDS1n,
+	UPFPGA_UP2_EXHAT_LVDS1p,
+	UPFPGA_UP2_SPI2_TXD,
+	UPFPGA_UP2_SPI2_RXD,
+	UPFPGA_UP2_SPI2_FS1,
+	UPFPGA_UP2_SPI2_FS0,
+	UPFPGA_UP2_SPI2_CLK,
+	UPFPGA_UP2_SPI1_TXD,
+};
+
+enum upboard_up2_reg1_fpgabit {
+	UPFPGA_UP2_SPI1_RXD,
+	UPFPGA_UP2_SPI1_FS1,
+	UPFPGA_UP2_SPI1_FS0,
+	UPFPGA_UP2_SPI1_CLK,
+	UPFPGA_UP2_BIT20,
+	UPFPGA_UP2_BIT21,
+	UPFPGA_UP2_BIT22,
+	UPFPGA_UP2_BIT23,
+	UPFPGA_UP2_PWM1,
+	UPFPGA_UP2_PWM0,
+	UPFPGA_UP2_EXHAT_LVDS0n,
+	UPFPGA_UP2_EXHAT_LVDS0p,
+	UPFPGA_UP2_I2C0_SCL,
+	UPFPGA_UP2_I2C0_SDA,
+	UPFPGA_UP2_I2C1_SCL,
+	UPFPGA_UP2_I2C1_SDA,
+};
+
+enum upboard_up2_reg2_fpgabit {
+	UPFPGA_UP2_EXHAT_LVDS3n,
+	UPFPGA_UP2_EXHAT_LVDS3p,
+	UPFPGA_UP2_EXHAT_LVDS4n,
+	UPFPGA_UP2_EXHAT_LVDS4p,
+	UPFPGA_UP2_EXHAT_LVDS5n,
+	UPFPGA_UP2_EXHAT_LVDS5p,
+	UPFPGA_UP2_I2S_SDO,
+	UPFPGA_UP2_I2S_SDI,
+	UPFPGA_UP2_I2S_WS_SYNC,
+	UPFPGA_UP2_I2S_BCLK,
+	UPFPGA_UP2_EXHAT_LVDS6n,
+	UPFPGA_UP2_EXHAT_LVDS6p,
+	UPFPGA_UP2_EXHAT_LVDS7n,
+	UPFPGA_UP2_EXHAT_LVDS7p,
+	UPFPGA_UP2_EXHAT_LVDS2n,
+	UPFPGA_UP2_EXHAT_LVDS2p,
+};
+
+static struct pinctrl_pin_desc upboard_up2_pins[] = {
+	UPBOARD_UP2_PIN_NAME(0, UART1_TXD),
+	UPBOARD_UP2_PIN_NAME(0, UART1_RXD),
+	UPBOARD_UP2_PIN_NAME(0, UART1_RTS),
+	UPBOARD_UP2_PIN_NAME(0, UART1_CTS),
+	UPBOARD_UP2_PIN_NAME(0, GPIO3),
+	UPBOARD_UP2_PIN_NAME(0, GPIO5),
+	UPBOARD_UP2_PIN_NAME(0, GPIO6),
+	UPBOARD_UP2_PIN_NAME(0, GPIO11),
+	UPBOARD_UP2_PIN_NAME(0, EXHAT_LVDS1n),
+	UPBOARD_UP2_PIN_NAME(0, EXHAT_LVDS1p),
+	UPBOARD_UP2_PIN_NAME(0, SPI2_TXD),
+	UPBOARD_UP2_PIN_NAME(0, SPI2_RXD),
+	UPBOARD_UP2_PIN_NAME(0, SPI2_FS1),
+	UPBOARD_UP2_PIN_NAME(0, SPI2_FS0),
+	UPBOARD_UP2_PIN_NAME(0, SPI2_CLK),
+	UPBOARD_UP2_PIN_NAME(0, SPI1_TXD),
+	UPBOARD_UP2_PIN_NAME(1, SPI1_RXD),
+	UPBOARD_UP2_PIN_NAME(1, SPI1_FS1),
+	UPBOARD_UP2_PIN_NAME(1, SPI1_FS0),
+	UPBOARD_UP2_PIN_NAME(1, SPI1_CLK),
+	UPBOARD_UP2_PIN_ANON(1, 4),
+	UPBOARD_UP2_PIN_ANON(1, 5),
+	UPBOARD_UP2_PIN_ANON(1, 6),
+	UPBOARD_UP2_PIN_ANON(1, 7),
+	UPBOARD_UP2_PIN_NAME(1, PWM1),
+	UPBOARD_UP2_PIN_NAME(1, PWM0),
+	UPBOARD_UP2_PIN_NAME(1, EXHAT_LVDS0n),
+	UPBOARD_UP2_PIN_NAME(1, EXHAT_LVDS0p),
+	UPBOARD_UP2_PIN_FUNC(1, I2C0_SCL, &upboard_i2c0_reg),
+	UPBOARD_UP2_PIN_FUNC(1, I2C0_SDA, &upboard_i2c0_reg),
+	UPBOARD_UP2_PIN_FUNC(1, I2C1_SCL, &upboard_i2c1_reg),
+	UPBOARD_UP2_PIN_FUNC(1, I2C1_SDA, &upboard_i2c1_reg),
+	UPBOARD_UP2_PIN_NAME(2, EXHAT_LVDS3n),
+	UPBOARD_UP2_PIN_NAME(2, EXHAT_LVDS3p),
+	UPBOARD_UP2_PIN_NAME(2, EXHAT_LVDS4n),
+	UPBOARD_UP2_PIN_NAME(2, EXHAT_LVDS4p),
+	UPBOARD_UP2_PIN_NAME(2, EXHAT_LVDS5n),
+	UPBOARD_UP2_PIN_NAME(2, EXHAT_LVDS5p),
+	UPBOARD_UP2_PIN_NAME(2, I2S_SDO),
+	UPBOARD_UP2_PIN_NAME(2, I2S_SDI),
+	UPBOARD_UP2_PIN_NAME(2, I2S_WS_SYNC),
+	UPBOARD_UP2_PIN_NAME(2, I2S_BCLK),
+	UPBOARD_UP2_PIN_NAME(2, EXHAT_LVDS6n),
+	UPBOARD_UP2_PIN_NAME(2, EXHAT_LVDS6p),
+	UPBOARD_UP2_PIN_NAME(2, EXHAT_LVDS7n),
+	UPBOARD_UP2_PIN_NAME(2, EXHAT_LVDS7p),
+	UPBOARD_UP2_PIN_NAME(2, EXHAT_LVDS2n),
+	UPBOARD_UP2_PIN_NAME(2, EXHAT_LVDS2p),
+};
+
+static const unsigned int upboard_up2_rpi_mapping[] = {
+	UPBOARD_UP2_BIT_TO_PIN(1, I2C0_SDA),
+	UPBOARD_UP2_BIT_TO_PIN(1, I2C0_SCL),
+	UPBOARD_UP2_BIT_TO_PIN(1, I2C1_SDA),
+	UPBOARD_UP2_BIT_TO_PIN(1, I2C1_SCL),
+	UPBOARD_UP2_BIT_TO_PIN(0, GPIO3),
+	UPBOARD_UP2_BIT_TO_PIN(0, GPIO11),
+	UPBOARD_UP2_BIT_TO_PIN(0, SPI2_CLK),
+	UPBOARD_UP2_BIT_TO_PIN(1, SPI1_FS1),
+	UPBOARD_UP2_BIT_TO_PIN(1, SPI1_FS0),
+	UPBOARD_UP2_BIT_TO_PIN(1, SPI1_RXD),
+	UPBOARD_UP2_BIT_TO_PIN(0, SPI1_TXD),
+	UPBOARD_UP2_BIT_TO_PIN(1, SPI1_CLK),
+	UPBOARD_UP2_BIT_TO_PIN(1, PWM0),
+	UPBOARD_UP2_BIT_TO_PIN(1, PWM1),
+	UPBOARD_UP2_BIT_TO_PIN(0, UART1_TXD),
+	UPBOARD_UP2_BIT_TO_PIN(0, UART1_RXD),
+	UPBOARD_UP2_BIT_TO_PIN(0, UART1_CTS),
+	UPBOARD_UP2_BIT_TO_PIN(0, UART1_RTS),
+	UPBOARD_UP2_BIT_TO_PIN(2, I2S_BCLK),
+	UPBOARD_UP2_BIT_TO_PIN(2, I2S_WS_SYNC),
+	UPBOARD_UP2_BIT_TO_PIN(2, I2S_SDI),
+	UPBOARD_UP2_BIT_TO_PIN(2, I2S_SDO),
+	UPBOARD_UP2_BIT_TO_PIN(0, GPIO6),
+	UPBOARD_UP2_BIT_TO_PIN(0, SPI2_FS1),
+	UPBOARD_UP2_BIT_TO_PIN(0, SPI2_RXD),
+	UPBOARD_UP2_BIT_TO_PIN(0, SPI2_TXD),
+	UPBOARD_UP2_BIT_TO_PIN(0, SPI2_FS0),
+	UPBOARD_UP2_BIT_TO_PIN(0, GPIO5),
+};
+
+/*
+ * UP Core board + CREX carrier board data
+ */
+
+#define UPBOARD_UPCORE_CREX_BIT_TO_PIN(r, id)				\
+	(UPBOARD_BIT_TO_PIN(r, UPFPGA_UPCORE_CREX_##id))
+
+#define UPBOARD_UPCORE_CREX_PIN_ANON(r, bit)				\
+	{								\
+		.number = UPBOARD_BIT_TO_PIN(r, bit),			\
+	}
+
+#define UPBOARD_UPCORE_CREX_PIN_NAME(r, id)				\
+	{								\
+		.number = UPBOARD_UPCORE_CREX_BIT_TO_PIN(r, id),	\
+		.name = #id,						\
+	}
+
+#define UPBOARD_UPCORE_CREX_PIN_FUNC(r, id, data)			\
+	{								\
+		.number = UPBOARD_UPCORE_CREX_BIT_TO_PIN(r, id),	\
+		.name = #id,						\
+		.drv_data = (void *)(data),				\
+	}
+
+enum upboard_upcore_crex_reg1_fpgabit {
+	UPFPGA_UPCORE_CREX_I2C0_SDA,
+	UPFPGA_UPCORE_CREX_I2C0_SCL,
+	UPFPGA_UPCORE_CREX_I2C1_SDA,
+	UPFPGA_UPCORE_CREX_I2C1_SCL,
+	UPFPGA_UPCORE_CREX_SPI2_CS0,
+	UPFPGA_UPCORE_CREX_SPI2_CS1,
+	UPFPGA_UPCORE_CREX_SPI2_MOSI,
+	UPFPGA_UPCORE_CREX_SPI2_MISO,
+	UPFPGA_UPCORE_CREX_SPI2_CLK,
+	UPFPGA_UPCORE_CREX_UART1_TXD,
+	UPFPGA_UPCORE_CREX_UART1_RXD,
+	UPFPGA_UPCORE_CREX_PWM0,
+	UPFPGA_UPCORE_CREX_PWM1,
+	UPFPGA_UPCORE_CREX_I2S2_FRM,
+	UPFPGA_UPCORE_CREX_I2S2_CLK,
+	UPFPGA_UPCORE_CREX_I2S2_RX,
+};
+
+enum upboard_upcore_crex_reg2_fpgabit {
+	UPFPGA_UPCORE_CREX_I2S2_TX,
+	UPFPGA_UPCORE_CREX_GPIO0,
+	UPFPGA_UPCORE_CREX_GPIO2,
+	UPFPGA_UPCORE_CREX_GPIO3,
+	UPFPGA_UPCORE_CREX_GPIO4,
+	UPFPGA_UPCORE_CREX_GPIO9,
+};
+
+static struct pinctrl_pin_desc upboard_upcore_crex_pins[] = {
+	UPBOARD_UPCORE_CREX_PIN_FUNC(0, I2C0_SDA, &upboard_i2c0_reg),
+	UPBOARD_UPCORE_CREX_PIN_FUNC(0, I2C0_SCL, &upboard_i2c0_reg),
+	UPBOARD_UPCORE_CREX_PIN_FUNC(0, I2C1_SDA, &upboard_i2c1_reg),
+	UPBOARD_UPCORE_CREX_PIN_FUNC(0, I2C1_SCL, &upboard_i2c1_reg),
+	UPBOARD_UPCORE_CREX_PIN_NAME(0, SPI2_CS0),
+	UPBOARD_UPCORE_CREX_PIN_NAME(0, SPI2_CS1),
+	UPBOARD_UPCORE_CREX_PIN_NAME(0, SPI2_MOSI),
+	UPBOARD_UPCORE_CREX_PIN_NAME(0, SPI2_MISO),
+	UPBOARD_UPCORE_CREX_PIN_NAME(0, SPI2_CLK),
+	UPBOARD_UPCORE_CREX_PIN_NAME(0, UART1_TXD),
+	UPBOARD_UPCORE_CREX_PIN_NAME(0, UART1_RXD),
+	UPBOARD_UPCORE_CREX_PIN_NAME(0, PWM0),
+	UPBOARD_UPCORE_CREX_PIN_NAME(0, PWM1),
+	UPBOARD_UPCORE_CREX_PIN_NAME(0, I2S2_FRM),
+	UPBOARD_UPCORE_CREX_PIN_NAME(0, I2S2_CLK),
+	UPBOARD_UPCORE_CREX_PIN_NAME(0, I2S2_RX),
+	/* register 1 */
+	UPBOARD_UPCORE_CREX_PIN_NAME(1, I2S2_TX),
+	UPBOARD_UPCORE_CREX_PIN_NAME(1, GPIO0),
+	UPBOARD_UPCORE_CREX_PIN_FUNC(1, GPIO2, &upboard_adc0_reg),
+	UPBOARD_UPCORE_CREX_PIN_NAME(1, GPIO3),
+	UPBOARD_UPCORE_CREX_PIN_NAME(1, GPIO4),
+	UPBOARD_UPCORE_CREX_PIN_NAME(1, GPIO9),
+};
+
+static unsigned int upboard_upcore_crex_rpi_mapping[] = {
+	UPBOARD_UPCORE_CREX_BIT_TO_PIN(0, I2C0_SDA),
+	UPBOARD_UPCORE_CREX_BIT_TO_PIN(0, I2C0_SCL),
+	UPBOARD_UPCORE_CREX_BIT_TO_PIN(0, I2C1_SDA),
+	UPBOARD_UPCORE_CREX_BIT_TO_PIN(0, I2C1_SCL),
+	UPBOARD_UPCORE_CREX_BIT_TO_PIN(1, GPIO0),
+	UPBOARD_UPCORE_CREX_BIT_TO_PIN(1, GPIO2),
+	UPBOARD_UPCORE_CREX_BIT_TO_PIN(1, GPIO3),
+	UPBOARD_UPCORE_CREX_BIT_TO_PIN(0, SPI2_CS1),
+	UPBOARD_UPCORE_CREX_BIT_TO_PIN(0, SPI2_CS0),
+	UPBOARD_UPCORE_CREX_BIT_TO_PIN(0, SPI2_MISO),
+	UPBOARD_UPCORE_CREX_BIT_TO_PIN(0, SPI2_MOSI),
+	UPBOARD_UPCORE_CREX_BIT_TO_PIN(0, SPI2_CLK),
+	UPBOARD_UPCORE_CREX_BIT_TO_PIN(0, PWM0),
+	UPBOARD_UPCORE_CREX_BIT_TO_PIN(0, PWM1),
+	UPBOARD_UPCORE_CREX_BIT_TO_PIN(0, UART1_TXD),
+	UPBOARD_UPCORE_CREX_BIT_TO_PIN(0, UART1_RXD),
+	UPBOARD_UPCORE_CREX_BIT_TO_PIN(1, GPIO9),
+	UPBOARD_UPCORE_CREX_BIT_TO_PIN(1, GPIO4),
+	UPBOARD_UPCORE_CREX_BIT_TO_PIN(0, I2S2_CLK),
+	UPBOARD_UPCORE_CREX_BIT_TO_PIN(0, I2S2_FRM),
+	UPBOARD_UPCORE_CREX_BIT_TO_PIN(0, I2S2_RX),
+	UPBOARD_UPCORE_CREX_BIT_TO_PIN(1, I2S2_TX),
+};
+
+/*
+ * UP Core board + CRST02 carrier board data
+ */
+
+#define upboard_upcore_crst02_pins        upboard_upcore_crex_pins
+#define upboard_upcore_crst02_rpi_mapping upboard_upcore_crex_rpi_mapping
+
+static int upboard_set_mux(struct pinctrl_dev *pctldev, unsigned int function,
+			   unsigned int group)
+{
+	return 0;
+};
+
+static int upboard_fpga_request_enable(struct pinctrl_dev *pctldev,
+				       struct pinctrl_gpio_range *range,
+				       unsigned int pin)
+{
+	const struct pin_desc * const pd = pin_desc_get(pctldev, pin);
+	const struct upboard_pin *p;
+	int ret;
+
+	if (!pd)
+		return -EINVAL;
+
+	p = pd->drv_data;
+	if (p->funcbit) {
+		ret = regmap_field_write(p->funcbit, 0);
+		if (ret)
+			return ret;
+	}
+
+	if (p->enbit) {
+		ret = regmap_field_write(p->enbit, 1);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+};
+
+static int upboard_fpga_request_free(struct pinctrl_dev *pctldev,
+				     unsigned int pin)
+{
+	const struct pin_desc * const pd = pin_desc_get(pctldev, pin);
+	const struct upboard_pin *p;
+	int ret;
+
+	if (!pd)
+		return -EINVAL;
+
+	p = pd->drv_data;
+	if (p->funcbit) {
+		ret = regmap_field_write(p->funcbit, 1);
+		if (ret)
+			return ret;
+	}
+
+	if (p->enbit) {
+		ret = regmap_field_write(p->enbit, 0);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+};
+
+static int upboard_fpga_set_direction(struct pinctrl_dev *pctldev,
+				      struct pinctrl_gpio_range *range,
+				      unsigned int pin, bool input)
+{
+	const struct pin_desc * const pd = pin_desc_get(pctldev, pin);
+	const struct upboard_pin *p;
+
+	if (!pd)
+		return -EINVAL;
+
+	p = pd->drv_data;
+	upboard_fpga_request_enable(pctldev, range, pin);
+
+	return regmap_field_write(p->dirbit, input);
+};
+
+static int upboard_get_functions_count(struct pinctrl_dev *pctldev)
+{
+	return 0;
+}
+
+static const char *upboard_get_function_name(struct pinctrl_dev *pctldev,
+					     unsigned int selector)
+{
+	return NULL;
+}
+
+static int upboard_get_function_groups(struct pinctrl_dev *pctldev,
+				       unsigned int selector,
+				       const char * const **groups,
+				       unsigned int *num_groups)
+{
+	*groups = NULL;
+	*num_groups = 0;
+	return 0;
+}
+
+static const struct pinmux_ops upboard_pinmux_ops = {
+	.get_functions_count = upboard_get_functions_count,
+	.get_function_groups = upboard_get_function_groups,
+	.get_function_name = upboard_get_function_name,
+	.set_mux = upboard_set_mux,
+	.gpio_request_enable = upboard_fpga_request_enable,
+	.gpio_set_direction = upboard_fpga_set_direction,
+};
+
+static int upboard_get_groups_count(struct pinctrl_dev *pctldev)
+{
+	return 0;
+}
+
+static const char *upboard_get_group_name(struct pinctrl_dev *pctldev,
+					  unsigned int selector)
+{
+	return NULL;
+}
+
+static const struct pinctrl_ops upboard_pinctrl_ops = {
+	.get_groups_count = upboard_get_groups_count,
+	.get_group_name = upboard_get_group_name,
+};
+
+static struct pinctrl_desc upboard_up_pinctrl_desc = {
+	.pins = upboard_up_pins,
+	.npins = ARRAY_SIZE(upboard_up_pins),
+	.pctlops = &upboard_pinctrl_ops,
+	.pmxops = &upboard_pinmux_ops,
+	.owner = THIS_MODULE,
+};
+
+static struct pinctrl_desc upboard_up2_pinctrl_desc = {
+	.pins = upboard_up2_pins,
+	.npins = ARRAY_SIZE(upboard_up2_pins),
+	.pctlops = &upboard_pinctrl_ops,
+	.pmxops = &upboard_pinmux_ops,
+	.owner = THIS_MODULE,
+};
+
+static struct pinctrl_desc upboard_upcore_crex_pinctrl_desc = {
+	.pins = upboard_upcore_crex_pins,
+	.npins = ARRAY_SIZE(upboard_upcore_crex_pins),
+	.pctlops = &upboard_pinctrl_ops,
+	.pmxops = &upboard_pinmux_ops,
+	.owner = THIS_MODULE,
+};
+
+static struct pinctrl_desc upboard_upcore_crst02_pinctrl_desc = {
+	.pins = upboard_upcore_crst02_pins,
+	.npins = ARRAY_SIZE(upboard_upcore_crst02_pins),
+	.pctlops = &upboard_pinctrl_ops,
+	.pmxops = &upboard_pinmux_ops,
+	.owner = THIS_MODULE,
+};
+
+static void upboard_alt_func_enable(struct gpio_chip *gc, const char *name)
+{
+	struct upboard_pinctrl *pctrl = container_of(gc, struct upboard_pinctrl, chip);
+	int offset[gc->ngpio];
+	int i, j, cnt;
+	bool input = false;
+
+	/* find all pins */
+	for (i = 0, cnt = 0; i < pctrl->pctldesc->npins; i++) {
+		if (strstr(pctrl->pctldesc->pins[i].name, name))
+			offset[cnt++] = i;
+	}
+	for (i = 0; i < cnt; i++) {
+		if (pinctrl_gpio_request(pctrl->pins[offset[i]].gpio) == -EBUSY)
+			return; /* this pin is being used */
+		pinctrl_gpio_free(pctrl->pins[offset[i]].gpio);
+	}
+	/* change to alternate function */
+	for (i = 0; i < cnt; i++) {
+		if (pctrl->pins[offset[i]].regs == NULL)
+			continue;
+
+		if (strstr(pctrl->pctldesc->pins[offset[i]].name, "I2C")) {
+			int mode = 1;
+			unsigned int val;
+
+			switch (pctrl->ident) {
+			case 15:
+				mode = 2;
+			break;
+			}
+
+			val = readl(pctrl->pins[offset[i]].regs) |
+				    mode<<PADCFG0_PMODE_SHIFT;
+			writel(val, pctrl->pins[offset[i]].regs);
+			upboard_fpga_request_free(pctrl->pctldev, offset[i]);
+			continue;
+		} else if (strstr(pctrl->pctldesc->pins[offset[i]].name,
+				  "UART")) {
+			int mode = 1;
+			unsigned int val;
+
+			switch (pctrl->ident) {
+			case 13:
+			case 14:
+				mode = 4;
+			break;
+			case 15:
+				mode = 2;
+			break;
+			}
+
+			val = readl(pctrl->pins[offset[i]].regs) |
+				    mode<<PADCFG0_PMODE_SHIFT;
+			writel(val, pctrl->pins[offset[i]].regs);
+		} else if (strstr(pctrl->pctldesc->pins[offset[i]].name,
+				  "SPI")) {
+			int mode = 1;
+			unsigned int val;
+
+			switch (pctrl->ident) {
+			case 15:
+				mode = 7;
+			break;
+			}
+
+			val = readl(pctrl->pins[offset[i]].regs) |
+				    mode<<PADCFG0_PMODE_SHIFT;
+			writel(val, pctrl->pins[offset[i]].regs);
+		} else if (strstr(pctrl->pctldesc->pins[offset[i]].name,
+				  "I2S")) {
+			int mode = 1;
+			unsigned int val;
+
+			switch (pctrl->ident) {
+			case 15:
+				mode = 4;
+			break;
+			}
+
+			val = readl(pctrl->pins[offset[i]].regs) |
+				    mode<<PADCFG0_PMODE_SHIFT;
+			writel(val, pctrl->pins[offset[i]].regs);
+		} else {
+			unsigned int val;
+
+			val = readl(pctrl->pins[offset[i]].regs) |
+				    1<<PADCFG0_PMODE_SHIFT;
+			writel(val, pctrl->pins[offset[i]].regs);
+		}
+
+		/* input pins */
+		if (strstr(pctrl->pctldesc->pins[offset[i]].name, "RX"))
+			input = true;
+		if (strstr(pctrl->pctldesc->pins[offset[i]].name, "CTS"))
+			input = true;
+		if (strstr(pctrl->pctldesc->pins[offset[i]].name, "ADC"))
+			input = true;
+		if (strstr(pctrl->pctldesc->pins[offset[i]].name, "MISO"))
+			input = true;
+		if (strstr(pctrl->pctldesc->pins[offset[i]].name, "DIN"))
+			input = true;
+		upboard_fpga_set_direction(pctrl->pctldev, NULL, offset[i],
+					   input);
+	}
+}
+
+static int upboard_rpi_to_native_gpio(struct gpio_chip *gc, unsigned int gpio)
+{
+	struct upboard_pinctrl *pctrl = container_of(gc, struct upboard_pinctrl, chip);
+	unsigned int pin = pctrl->rpi_mapping[gpio];
+	struct pinctrl_gpio_range *range;
+
+	range = pinctrl_find_gpio_range_from_pin(pctrl->pctldev, pin);
+	if (!range)
+		return -ENODEV;
+
+	return range->base;
+}
+
+static int upboard_gpio_request(struct gpio_chip *gc, unsigned int offset)
+{
+	struct upboard_pinctrl *pctrl = container_of(gc, struct upboard_pinctrl, chip);
+	int gpio = upboard_rpi_to_native_gpio(gc, offset);
+
+	if (gpio < 0)
+		return gpio;
+
+	return pinctrl_gpio_request(gpio);
+}
+
+static void upboard_gpio_free(struct gpio_chip *gc, unsigned int offset)
+{
+	struct upboard_pinctrl *pctrl = container_of(gc, struct upboard_pinctrl, chip);
+	int gpio = upboard_rpi_to_native_gpio(gc, offset);
+	unsigned int pin = pctrl->rpi_mapping[offset];
+	char name[strlen(pctrl->pctldesc->pins[pin].name)];
+	char *p;
+
+	if (gpio < 0)
+		return;
+
+	pinctrl_gpio_free(gpio);
+
+	strcpy(name, pctrl->pctldesc->pins[pin].name);
+	p = name;
+	upboard_alt_func_enable(gc, strsep(&p, "_"));
+}
+
+static int upboard_gpio_get(struct gpio_chip *gc, unsigned int offset)
+{
+	int gpio = upboard_rpi_to_native_gpio(gc, offset);
+
+	if (gpio < 0)
+		return gpio;
+
+	return gpiod_get_value(gpio_to_desc(gpio));
+}
+
+static void upboard_gpio_set(struct gpio_chip *gc, unsigned int offset,
+			     int value)
+{
+	struct upboard_pinctrl *pctrl = container_of(gc, struct upboard_pinctrl, chip);
+	unsigned int pin = pctrl->rpi_mapping[offset];
+	int gpio = upboard_rpi_to_native_gpio(gc, offset);
+
+	if (gpio < 0)
+		return;
+
+	gpiod_set_value(gpio_to_desc(gpio), value);
+
+	/* APL03 open drain GPIO */
+	if (pctrl->ident == 9) {
+		if (pin == 0 || pin == 1) {
+			int val = readl(pctrl->pins[pin].regs);
+
+			if (value)
+				val |= PADCFG0_GPIOTXDIS;
+			else
+				val &= ~PADCFG0_GPIOTXDIS;
+			writel(val, pctrl->pins[pin].regs);
+		}
+	}
+}
+
+static int upboard_gpio_direction_input(struct gpio_chip *gc,
+					unsigned int offset)
+{
+	int gpio = upboard_rpi_to_native_gpio(gc, offset);
+	struct upboard_pinctrl *pctrl = container_of(gc, struct upboard_pinctrl, chip);
+	unsigned int pin = pctrl->rpi_mapping[offset];
+	struct pinctrl_gpio_range *range;
+
+	range = pinctrl_find_gpio_range_from_pin(pctrl->pctldev, pin);
+	upboard_fpga_set_direction(pctrl->pctldev, range, pin, true);
+
+	if (gpio < 0)
+		return gpio;
+
+	return gpiod_direction_input(gpio_to_desc(gpio));
+}
+
+static int upboard_gpio_direction_output(struct gpio_chip *gc,
+					 unsigned int offset, int value)
+{
+	int gpio = upboard_rpi_to_native_gpio(gc, offset);
+	struct upboard_pinctrl *pctrl = container_of(gc, struct upboard_pinctrl, chip);
+	unsigned int pin = pctrl->rpi_mapping[offset];
+	struct pinctrl_gpio_range *range;
+
+	range = pinctrl_find_gpio_range_from_pin(pctrl->pctldev, pin);
+	upboard_fpga_set_direction(pctrl->pctldev, range, pin, false);
+
+	if (gpio < 0)
+		return gpio;
+
+	return gpiod_direction_output(gpio_to_desc(gpio), value);
+}
+
+static struct gpio_chip upboard_gpio_chip = {
+	.label = "Raspberry Pi compatible UP GPIO",
+	.base = 0,
+	.request = upboard_gpio_request,
+	.free = upboard_gpio_free,
+	.get = upboard_gpio_get,
+	.set = upboard_gpio_set,
+	.direction_input = upboard_gpio_direction_input,
+	.direction_output = upboard_gpio_direction_output,
+	.owner = THIS_MODULE,
+};
+
+/* DMI Matches for older bios without fpga initialization */
+static const struct dmi_system_id upboard_dmi_table[] __initconst = {
+	{
+		.matches = { /* UP */
+			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "AAEON"),
+			DMI_EXACT_MATCH(DMI_BOARD_NAME, "UP-CHT01"),
+			DMI_EXACT_MATCH(DMI_BOARD_VERSION, "V0.4"),
+		},
+	},
+	{
+		.matches = { /* UP2 */
+			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "AAEON"),
+			DMI_EXACT_MATCH(DMI_BOARD_NAME, "UP-APL01"),
+			DMI_EXACT_MATCH(DMI_BOARD_VERSION, "V0.3"),
+		},
+	},
+	{
+		.ident = 9,
+		.matches = { /* UP2 */
+			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "AAEON"),
+			DMI_EXACT_MATCH(DMI_BOARD_NAME, "UP-APL03"),
+		},
+	},
+	{
+		.ident = 13,
+		.matches = { /* UP 6000 */
+			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "AAEON"),
+			DMI_EXACT_MATCH(DMI_BOARD_NAME, "UPN-EHL01"),
+		},
+	},
+	{
+		.ident = 14,
+		.matches = { /* UP squared v2 */
+			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "AAEON"),
+			DMI_EXACT_MATCH(DMI_BOARD_NAME, "UPS-EHL01"),
+		},
+	},
+	{
+		.ident = 15,
+		.matches = { /* UP Xtreme i12 */
+			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "AAEON"),
+			DMI_EXACT_MATCH(DMI_BOARD_NAME, "UPX-ADLP01"),
+		},
+	},
+	{ }	/* Terminating entry */
+};
+
+bool regmap_check_writeable(struct upboard_fpga *fpga, unsigned int reg)
+{
+	if (!fpga)
+		return -ENOMEM;
+
+	if (fpga->regmapconf->max_register &&
+	    reg > fpga->regmapconf->max_register)
+		return false;
+
+	if (fpga->regmapconf->writeable_reg)
+		return fpga->regmapconf->writeable_reg(fpga->dev, reg);
+
+	if (fpga->regmapconf->wr_table)
+		return regmap_check_range_table(fpga->regmap, reg,
+						fpga->regmapconf->wr_table);
+
+	return true;
+}
+
+static void __iomem *upboard_get_regs(struct gpio_chip *gc, unsigned int pin,
+				      unsigned int reg)
+{
+	struct platform_device *pdev = to_platform_device(gc->parent);
+	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	size_t nregs;
+	void __iomem *base;
+	u32 offset, value;
+
+	/* UP ADL/TGL */
+	if (strstr(dev_name(&pdev->dev), "INTC1055:00") ||
+	    strstr(dev_name(&pdev->dev), "INT34C5:00")) {
+		struct intel_pinctrl *intel_pctrl = gpiochip_get_data(gc);
+		struct pinctrl_dev *pctldev;
+		struct pinctrl_gpio_range *range;
+
+		pctldev = intel_pctrl->pctldev;
+		if (pctldev == NULL)
+			return NULL;
+
+		range = pinctrl_find_gpio_range_from_pin(pctldev, pin);
+		if (range)
+			pin = pin - range->pin_base;
+		else
+			return NULL;
+
+		if (range->pin_base < 67)
+			res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+		else if (range->pin_base < 171)
+			res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+		else if (range->pin_base < 260)
+			res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+		else
+			res = platform_get_resource(pdev, IORESOURCE_MEM, 3);
+	}
+
+	if (IS_ERR(res)) {
+		dev_err(gc->parent, "upboard resource get failed");
+		return NULL;
+	}
+
+	base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+	offset = readl(base + PADBAR);
+	value = readl(base + REVID);
+
+	if (((value & REVID_MASK) >> REVID_SHIFT) >= 0x94)
+		nregs = 4;
+	else
+		nregs = 2;
+
+	return base + offset + reg + pin * nregs * 4;
+}
+
+int upboard_acpi_node_pin_mapping(struct upboard_fpga *fpga,
+				  struct upboard_pinctrl *pctrl,
+				  const char *propname,
+				  const char *pinctl_name,
+				  unsigned int pin_offset)
+{
+	struct gpio_descs *descs;
+	int ret, i;
+
+	descs = devm_gpiod_get_array(fpga->dev, propname, GPIOD_ASIS);
+	if (IS_ERR(descs)) {
+		ret = PTR_ERR(descs);
+		if (ret != -EPROBE_DEFER)
+			dev_err(fpga->dev, "Failed to get %s gpios", propname);
+		return ret;
+	}
+
+	for (i = 0; i < descs->ndescs; i++) {
+		struct gpio_desc *desc = descs->desc[i];
+		struct gpio_chip *gc = gpiod_to_chip(desc);
+
+		pctrl->pins[i].gpio = desc_to_gpio(desc);
+		pctrl->pins[i].base = gc->base;
+		pctrl->pins[i].irq = gpiod_to_irq(desc);
+		pctrl->pins[i].regs =
+			upboard_get_regs(gc,
+					 desc_to_gpio(desc) - gc->base,
+					 PADCFG0);
+
+		/* The GPIOs may not be contiguous, so add them 1-by-1 */
+		ret = gpiochip_add_pin_range(gpiod_to_chip(desc), pinctl_name,
+					     desc_to_gpio(desc) - gc->base,
+					     pin_offset + i, 1);
+		if (ret)
+			return ret;
+	}
+
+	/* dispose acpi resource */
+	devm_gpiod_put_array(fpga->dev, descs);
+
+	return ret;
+}
+
+static void upboard_irq_ack(struct irq_data *d)
+{
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+
+	dev_info(NULL, "upboard_irq_ack");
+}
+
+static void upboard_irq_mask(struct irq_data *d)
+{
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+
+	dev_info(NULL, "upboard_irq_mask");
+}
+
+static void upboard_irq_unmask(struct irq_data *d)
+{
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+
+	dev_info(NULL, "upboard_irq_unmask");
+}
+
+static int upboard_irq_type(struct irq_data *d, unsigned int type)
+{
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+
+	dev_info(NULL, "upboard_irq_type");
+
+	return 0;
+}
+
+static irqreturn_t upboard_gpio_irq_handler(int irq, void *data)
+{
+	generic_handle_irq(irq);
+	return IRQ_HANDLED;
+}
+
+static int upboard_irq_chip_set_type(struct irq_data *data, unsigned int type)
+{
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(data);
+	struct upboard_pinctrl *pctrl = container_of(gc, struct upboard_pinctrl, chip);
+	unsigned int offset = irqd_to_hwirq(data);
+
+	data->parent_data = irq_get_irq_data(pctrl->pins[offset].irq);
+	if (data->parent_data) {
+		dev_info(NULL, "%s: no NULL", __func__);
+		return irq_chip_set_type_parent(data, type);
+	}
+
+	dev_info(NULL, "%s: NULL", __func__);
+	return 0;
+}
+
+static struct irq_chip upboard_gpio_irqchip = {
+	.name = "upboard-gpio-irq",
+	.irq_ack = upboard_irq_ack,
+	.irq_mask = upboard_irq_mask,
+	.irq_unmask = upboard_irq_unmask,
+	.irq_set_type = upboard_irq_chip_set_type,
+	.flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE,
+};
+
+static int __init upboard_pinctrl_probe(struct platform_device *pdev)
+{
+	struct upboard_fpga * const fpga = dev_get_drvdata(pdev->dev.parent);
+	struct acpi_device * const adev = ACPI_COMPANION(&pdev->dev);
+	struct pinctrl_desc *pctldesc;
+	struct upboard_pinctrl *pctrl;
+	struct upboard_pin *pins;
+	const struct dmi_system_id *system_id;
+	const char *hid;
+	const unsigned int *rpi_mapping;
+	unsigned int ngpio;
+	int ret;
+	int i;
+
+	if (!fpga)
+		return -EINVAL;
+
+	if (!adev)
+		return -ENODEV;
+
+	hid = acpi_device_hid(adev);
+	if (!strcmp(hid, "AANT0F00") || !strcmp(hid, "AANT0F04") ||
+	    !strcmp(hid, "AANT0000")) {
+		pctldesc = &upboard_up_pinctrl_desc;
+		rpi_mapping = upboard_up_rpi_mapping;
+		ngpio  = ARRAY_SIZE(upboard_up_rpi_mapping);
+	} else if (!strcmp(hid, "AANT0F01")) {
+		pctldesc = &upboard_up2_pinctrl_desc;
+		rpi_mapping = upboard_up2_rpi_mapping;
+		ngpio  = ARRAY_SIZE(upboard_up2_rpi_mapping);
+	} else if (!strcmp(hid, "AANT0F02")) {
+		pctldesc = &upboard_upcore_crex_pinctrl_desc;
+		rpi_mapping = upboard_upcore_crex_rpi_mapping;
+		ngpio  = ARRAY_SIZE(upboard_upcore_crex_rpi_mapping);
+	} else if (!strcmp(hid, "AANT0F03")) {
+		pctldesc = &upboard_upcore_crst02_pinctrl_desc;
+		rpi_mapping = upboard_upcore_crst02_rpi_mapping;
+		ngpio  = ARRAY_SIZE(upboard_upcore_crst02_rpi_mapping);
+	} else
+		return -ENODEV;
+
+	pctldesc->name = dev_name(&pdev->dev);
+
+	pins = devm_kzalloc(&pdev->dev,
+			    sizeof(*pins) * pctldesc->npins,
+			    GFP_KERNEL);
+	if (!pins)
+		return -ENOMEM;
+
+	/* initialise pins */
+	for (i = 0; i < pctldesc->npins; i++) {
+		struct upboard_pin *pin = &pins[i];
+		struct pinctrl_pin_desc *pd =
+			(struct pinctrl_pin_desc *) &pctldesc->pins[i];
+		struct reg_field fldconf = {0};
+		unsigned int regoff = (pd->number / UPFPGA_REGISTER_SIZE);
+		unsigned int lsb = pd->number % UPFPGA_REGISTER_SIZE;
+
+		pin->funcbit = NULL;
+
+		if (pd->drv_data) {
+			fldconf = *(struct reg_field *)pd->drv_data;
+			if (!regmap_check_writeable(fpga, fldconf.reg))
+				return -EINVAL;
+
+			pin->funcbit = devm_regmap_field_alloc(&pdev->dev,
+							       fpga->regmap,
+							       fldconf);
+			if (IS_ERR(pin->funcbit))
+				return PTR_ERR(pin->funcbit);
+		}
+
+		pin->enbit = NULL;
+		fldconf.reg = UPFPGA_REG_GPIO_EN0 + regoff;
+		fldconf.lsb = lsb;
+		fldconf.msb = lsb;
+
+		/* some platform don't have enable bit, ignore if not present */
+		if (regmap_check_writeable(fpga, fldconf.reg)) {
+			pin->enbit = devm_regmap_field_alloc(&pdev->dev,
+							     fpga->regmap,
+							     fldconf);
+			if (IS_ERR(pin->enbit))
+				return PTR_ERR(pin->enbit);
+		}
+
+		fldconf.reg = UPFPGA_REG_GPIO_DIR0 + regoff;
+		fldconf.lsb = lsb;
+		fldconf.msb = lsb;
+
+		if (!regmap_check_writeable(fpga, fldconf.reg))
+			return -EINVAL;
+
+		pin->dirbit = devm_regmap_field_alloc(&pdev->dev,
+						      fpga->regmap,
+						      fldconf);
+		if (IS_ERR(pin->dirbit))
+			return PTR_ERR(pin->dirbit);
+
+		pd->drv_data = pin;
+	}
+
+	/* create a new pinctrl device and register it */
+	pctrl = devm_kzalloc(&pdev->dev, sizeof(*pctrl), GFP_KERNEL);
+	if (!pctrl)
+		return -ENOMEM;
+
+	pctrl->regmap = fpga->regmap;
+	pctrl->rpi_mapping = rpi_mapping;
+	pctrl->chip = upboard_gpio_chip;
+	pctrl->pctldesc = pctldesc;
+	pctrl->chip.parent = &pdev->dev;
+	pctrl->chip.ngpio = ngpio;
+	pctrl->pins = pins;
+	pctrl->chip.irq.chip = &upboard_gpio_irqchip;
+
+	ret = devm_gpiochip_add_data(&pdev->dev, &pctrl->chip, pctrl);
+	if (ret)
+		return ret;
+
+	pctrl->pctldev = devm_pinctrl_register(&pdev->dev, pctldesc, pctrl);
+	if (IS_ERR(pctrl->pctldev))
+		return PTR_ERR(pctrl->pctldev);
+
+	/* add acpi pin mapping according to external-gpios key */
+	ret = upboard_acpi_node_pin_mapping(fpga, pctrl,
+					"external",
+					dev_name(&pdev->dev),
+					0);
+	if (ret)
+		return ret;
+
+	upboard_alt_func_enable(&pctrl->chip, "I2C");
+	upboard_alt_func_enable(&pctrl->chip, "SPI");
+	upboard_alt_func_enable(&pctrl->chip, "UART");
+	upboard_alt_func_enable(&pctrl->chip, "I2S");
+	upboard_alt_func_enable(&pctrl->chip, "PWM");
+	upboard_alt_func_enable(&pctrl->chip, "ADC");
+	upboard_alt_func_enable(&pctrl->chip, "NONAME"); /* for UP2 I2C pins */
+
+	return ret;
+}
+
+static struct platform_driver upboard_pinctrl_driver = {
+	.driver = {
+		.name = "upboard-pinctrl",
+	},
+};
+module_platform_driver_probe(upboard_pinctrl_driver, upboard_pinctrl_probe);
+
+MODULE_AUTHOR("Gary Wang <garywang@aaeon.com.tw>");
+MODULE_AUTHOR("Javier Arteaga <javier@emutex.com>");
+MODULE_AUTHOR("Dan O'Donovan <dan@emutex.com>");
+MODULE_AUTHOR("Nicola Lunghi <nicolal@emutex.com>");
+MODULE_DESCRIPTION("UP Board HAT pin controller driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:upboard-pinctrl");
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [RFC 3/3] leds: Add support for UP board CPLD onboard LEDS
  2022-12-07 16:33 [RFC 0/3] Add support control UP board CPLD/FPGA pin control larry.lai
  2022-12-07 16:33 ` [RFC 1/3] mfd: Add support for UP board CPLD/FPGA larry.lai
  2022-12-07 16:33 ` [RFC 2/3] pinctrl: Add support pin control " larry.lai
@ 2022-12-07 16:33 ` larry.lai
  2022-12-07 21:09 ` [RFC 0/3] Add support control UP board CPLD/FPGA pin control Linus Walleij
  2022-12-07 21:36 ` Andy Shevchenko
  4 siblings, 0 replies; 12+ messages in thread
From: larry.lai @ 2022-12-07 16:33 UTC (permalink / raw)
  To: lee, andriy.shevchenko, linus.walleij, pavel
  Cc: linux-kernel, linux-gpio, linux-leds, GaryWang, musa.lin,
	jack.chang, noah.hung, larry.lai

The UP boards come with a few FPGA-controlled onboard LEDs:
* UP Board: yellow, green, red
* UP Squared: blue, yellow, green, red

This patch depends on patch "mfd: Add support for UP board CPLD/FPGA".

Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: larry.lai <larry.lai@yunjingtech.com>
---
PATCH V3 -> RFC 2022/11/23: Update the changes Copyright.
PATCH V1 -> V3: There is no change.
PATCH --> PATCH V1: Refer 2022/10/03 Andy Shevchenko review, cleaned up
coding style.
---
 drivers/leds/Kconfig        | 10 +++++
 drivers/leds/Makefile       |  1 +
 drivers/leds/leds-upboard.c | 79 +++++++++++++++++++++++++++++++++++++
 3 files changed, 90 insertions(+)
 create mode 100644 drivers/leds/leds-upboard.c

diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 499d0f215a8b..d9d533cb38ca 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -872,6 +872,16 @@ source "drivers/leds/flash/Kconfig"
 comment "RGB LED drivers"
 source "drivers/leds/rgb/Kconfig"
 
+config LEDS_UPBOARD
+	tristate "LED support for the UP board"
+	depends on LEDS_CLASS
+	depends on MFD_INTEL_UPBOARD_FPGA
+	help
+	  This option enables support for the UP board LEDs.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called leds-upboard.
+
 comment "LED Triggers"
 source "drivers/leds/trigger/Kconfig"
 
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 4fd2f92cd198..e72956645646 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -83,6 +83,7 @@ obj-$(CONFIG_LEDS_TI_LMU_COMMON)	+= leds-ti-lmu-common.o
 obj-$(CONFIG_LEDS_TLC591XX)		+= leds-tlc591xx.o
 obj-$(CONFIG_LEDS_TPS6105X)		+= leds-tps6105x.o
 obj-$(CONFIG_LEDS_TURRIS_OMNIA)		+= leds-turris-omnia.o
+obj-$(CONFIG_LEDS_UPBOARD)		+= leds-upboard.o
 obj-$(CONFIG_LEDS_WM831X_STATUS)	+= leds-wm831x-status.o
 obj-$(CONFIG_LEDS_WM8350)		+= leds-wm8350.o
 obj-$(CONFIG_LEDS_WRAP)			+= leds-wrap.o
diff --git a/drivers/leds/leds-upboard.c b/drivers/leds/leds-upboard.c
new file mode 100644
index 000000000000..7a597fdad43c
--- /dev/null
+++ b/drivers/leds/leds-upboard.c
@@ -0,0 +1,79 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * UP Board FPGA-based LED driver
+ *
+ * Copyright (c) 2017, Emutex Ltd. All rights reserved.
+ * Copyright (c) 2022, YunJingTech Ltd.
+ *
+ * Author: Javier Arteaga <javier@emutex.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/leds.h>
+#include <linux/mfd/upboard-fpga.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+struct upboard_led {
+	struct led_classdev cdev;
+	struct regmap_field *field;
+};
+
+static enum led_brightness upboard_led_brightness_get(struct led_classdev *cdev)
+{
+	struct upboard_led *led = container_of(cdev, struct upboard_led, cdev);
+	int brightness = 0;
+
+	regmap_field_read(led->field, &brightness);
+
+	return brightness;
+};
+
+static void upboard_led_brightness_set(struct led_classdev *cdev, enum led_brightness brightness)
+{
+	struct upboard_led *led = container_of(cdev, struct upboard_led, cdev);
+
+	regmap_field_write(led->field, brightness != LED_OFF);
+};
+
+static int __init upboard_led_probe(struct platform_device *pdev)
+{
+	struct upboard_fpga * const up_fpga = dev_get_drvdata(pdev->dev.parent);
+	struct reg_field fldconf = {
+		.reg = UPFPGA_REG_FUNC_EN0,
+	};
+	struct upboard_led_data * const pdata = pdev->dev.platform_data;
+	struct upboard_led *led;
+
+	led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL);
+	if (!led)
+		return -ENOMEM;
+
+	fldconf.lsb = pdata->bit;
+	fldconf.msb = pdata->bit;
+	led->field = devm_regmap_field_alloc(&pdev->dev, up_fpga->regmap, fldconf);
+	if (IS_ERR(led->field))
+		return PTR_ERR(led->field);
+
+	led->cdev.brightness_get = upboard_led_brightness_get;
+	led->cdev.brightness_set = upboard_led_brightness_set;
+	led->cdev.name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "upboard:%s:",
+					pdata->colour);
+	if (!led->cdev.name)
+		return -ENOMEM;
+
+	return devm_led_classdev_register(&pdev->dev, &led->cdev);
+};
+
+static struct platform_driver upboard_led_driver = {
+	.driver = {
+		.name = "upboard-led",
+	},
+};
+module_platform_driver_probe(upboard_led_driver, upboard_led_probe);
+
+MODULE_AUTHOR("Javier Arteaga <javier@emutex.com>");
+MODULE_DESCRIPTION("UP Board LED driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:upboard-led");
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [RFC 0/3] Add support control UP board CPLD/FPGA pin control
  2022-12-07 16:33 [RFC 0/3] Add support control UP board CPLD/FPGA pin control larry.lai
                   ` (2 preceding siblings ...)
  2022-12-07 16:33 ` [RFC 3/3] leds: Add support for UP board CPLD onboard LEDS larry.lai
@ 2022-12-07 21:09 ` Linus Walleij
  2022-12-07 21:33   ` Andy Shevchenko
  2022-12-07 21:36 ` Andy Shevchenko
  4 siblings, 1 reply; 12+ messages in thread
From: Linus Walleij @ 2022-12-07 21:09 UTC (permalink / raw)
  To: larry.lai
  Cc: lee, andriy.shevchenko, pavel, linux-kernel, linux-gpio,
	linux-leds, GaryWang, musa.lin, jack.chang, noah.hung

On Wed, Dec 7, 2022 at 5:36 PM larry.lai <larry.lai@yunjingtech.com> wrote:

> The UP board <http://www.upboard.com> is the computer board for
> Professional Makers and Industrial Applications. We want to upstream
> the UP board 40-pin GP-bus Kernel driver for giving the users better
> experience on the software release. (not just download from UP board
> github)
>
> These patches are generated from the Linux kernel mainline tag v6.0.

Why are these patches tagged RFC now? Weird.

Came to think of this:
Shouldn't the subdrivers for pin control LED etc have:
default MFD_INTEL_UPBOARD_FPGA
i.e become y if the core driver is y, becomes m if the core driver is m.
Of course it is possible to run around in menuconfig and activate them
all manually interactively and be frustrated that something is missing
still but setting them default like this saves everybody's time. Activate
the MFD core driver and everything else comes with it, then it can
be turned off at request.

Yours,
Linus Walleij

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC 1/3] mfd: Add support for UP board CPLD/FPGA
  2022-12-07 16:33 ` [RFC 1/3] mfd: Add support for UP board CPLD/FPGA larry.lai
@ 2022-12-07 21:10   ` Andy Shevchenko
  2023-04-25 15:28     ` 回覆: " Larry Lai
  0 siblings, 1 reply; 12+ messages in thread
From: Andy Shevchenko @ 2022-12-07 21:10 UTC (permalink / raw)
  To: larry.lai
  Cc: lee, linus.walleij, pavel, linux-kernel, linux-gpio, linux-leds,
	GaryWang, musa.lin, jack.chang, noah.hung, Javier Arteaga,
	Nicola Lunghi

On Thu, Dec 08, 2022 at 12:33:57AM +0800, larry.lai wrote:
> The UP Squared board <http://www.upboard.com> implements certain
> features (pin control, onboard LEDs or CEC) through an on-board CPLD/FPGA.
> 
> This mfd driver implements the line protocol to read and write registers
> from the FPGA through regmap. The register address map is also included.
> 
> The UP Boards provide a few I/O pin headers (for both GPIO and
> functions), including a 40-pin Raspberry Pi compatible header.
> 
> This patch implements support for the FPGA-based pin controller that
> manages direction and enable state for those header pins.
> 
> Partial support UP boards:
> * UP core + CREX
> * UP core + CRST02

...

> +#include <linux/acpi.h>
> +#include <linux/dmi.h>

> +#include <linux/gpio.h>

I'm not sure if you read my previous emails regarding the topic.
This header must not be in the new code.

> +#include <linux/kernel.h>
> +#include <linux/leds.h>
> +#include <linux/mfd/core.h>
> +#include <linux/mfd/upboard-fpga.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>


Missing bits.h and err.h at least.

...

> +struct upboard_fpga_data {
> +	const struct regmap_config *regmapconf;

No need to repeat regmap twice.

> +	const struct mfd_cell *cells;
> +	size_t ncells;
> +};

...

> +#define MENUFACTURER_ID_MASK		0xFF

GENMASK()?

...

> +#define FIRMWARE_ID_MASK		0xF

Ditto.

...

> +/* Apollo Lake GPIO pin number mapping to FPGA LED */
> +#define APL_GPIO_218			507

No way. It should be addressed as GPIO chip reference and relative pin
(or GPIO, whichever suits better for your purposes) number.

...

> +/* For UP Board Series FPGA register read/write protocols 		      */
> +/* EMUTEX specs: 						              */
> +/* D0   D1  D2  D3  D4  D5  D6  D7  D8  D9 .... D22  D23                      */
> +/* [RW][        address           ][	    DATA        ]                     */
> +
> +/* Read Sequence:                                                             */
> +/*      ___   ____________________________________________________   _________*/
> +/* clr:    \_/ <--low-pulse does start the write-readback         \_/<--start */
> +/*	          sequence with partital reset of internal 	  new sequence*/
> +/*	          registers but the CONF-REG. 	  		              */
> +/*        ____________________________________________________________________*/
> +/* rst: _/       _   _   _        _   _   _   __       __   __   _            */
> +/* stb: STB#1->_/1\_/2\_/3\_...._/7\_/8\_/9\_/10\_..../23\_/24\_/<-STB#25 edge*/
> +/*						                   is needed  */
> +/*	  						           to ACK     */
> +/*             (D0 - D7 stb rising latch)                                     */
> +/* data_in:     D0  D1  D2  .... D6  D7  don't ........ care(DC)              */
> +/* data_out:    don't ...........care(DC)  D8   D9 ....  D22  D23   	      */
> +/*					  (D8 - D23 stb falling latch) 	      */
> +/* flag_Read:				  _________...._________              */
> +/*      __DC_   ____________...._________/                      \_            */
> +/* counter:								      */
> +/*    [00]DC[00][01][02] ............[08][9][10]............[24][00]	      */
> +/* CONF-REG:					                              */
> +/*    [00] [				CONF-REG               ]              */
> +/* wreg:   							              */
> +/*    [00]DC[00][  wreg=SHFT(wreg)  ][ADR][DATA][wreg=SHFT(wreg]	      */
> +/* wreg2:		  						      */
> +/*    					  [	   (COPY)=ADDR ]	      */

This has too many /* */ and TABs vs space mix... Please, fix it.

Is it SPI 24-bit bit-banging? Why spi-gpio can't be utilized for it?

...

> +/* Write Sequence:                                                   	      */
> +/*      ___   ____________________________________________________   _________*/
> +/* clr:    \_/ <--low-pulse does start the write-readback         \_/<--start */
> +/*	          sequence with partital reset of internal 	  new sequence*/
> +/*	          registers but the CONF-REG. 	  			      */
> +/*        ____________________________________________________________________*/
> +/* rst: _/       _   _   _        _   _   _   __       __   __   _            */
> +/* stb: STB#1->_/1\_/2\_/3\_...._/7\_/8\_/9\_/10\_..../23\_/24\_/<-STB#25 edge*/
> +/*						                   is needed  */
> +/*								   to ACK     */
> +/*             (D0 - D23 stb rising latch)                                    */
> +/* data_in:     D0  D1  D2  .... D6  D7  D8  D9 ....  D22  D23                */
> +/* data_out:    don't ................................care (DC)               */
> +/* flag_Read:			 					      */
> +/*      __DC_   ____________....__________________________________            */
> +/* counter:								      */
> +/*    [00]DC[00][01][02] ............[08][9][10]............[24][00]          */
> +/* wreg:								      */
> +/*    [00]DC[00][wreg=SHFT(wreg)&dat_in ][SHFT(wreg)&dat_in][DAT]             */
> +/* wreg2:							              */
> +/*    					 [     (COPY)=ADDR     ]	      */
> +/* CONF-REG:								      */
> +/*    [00] [	     CONF-REG = OLD VALUE		       ][CONF-REG=DAT]*/


Same comments as per above.

...

> +		gpiod_set_value(fpga->datain_gpio, (reg >> i) & 0x1);

!!(reg & BIT(i))

...

> +		gpiod_set_value(fpga->datain_gpio, (val >> i) & 0x1);

Ditto.

But see above.

...

> +static struct gpio_led upboard_gpio_leds[] = {
> +	{
> +		.name = "upboard:blue:",
> +		.gpio = APL_GPIO_218,

You must understand that it won't work with dynamic GPIO bases which will be
enabled in v6.2-rc1. And even in general it must not be like this.

> +		.default_state = LEDS_GPIO_DEFSTATE_KEEP,
> +	},
> +};

...

> +	enum gpiod_flags flags;
> +
> +	flags = fpga->uninitialised ? GPIOD_OUT_LOW : GPIOD_ASIS;

Can be united.

...

> +	/*
> +	 * The SoC pinctrl driver may not support reserving the GPIO line for
> +	 * FPGA reset without causing an undesired reset pulse. This will clear
> +	 * any settings on the FPGA, so only do it if we must.
> +	 * Reset gpio defaults HIGH, get gpio and set to LOW, then set back to
> +	 * HIGH as a pulse.
> +	 */
> +	if (fpga->uninitialised) {
> +		fpga->reset_gpio = devm_gpiod_get(fpga->dev, "reset", GPIOD_OUT_LOW);
> +		if (IS_ERR(fpga->reset_gpio))
> +			return PTR_ERR(fpga->reset_gpio);

No sleep for the hardware to be really reset?

> +		gpiod_set_value(fpga->reset_gpio, 1);
> +	}

> +/*
> + * MFD upboard-fpga is acpi driver and can recognize the AANT ID from different

ACPI

> + * kind of upboards. We get the led gpio initialized information from this

LED GPIO

> + * then add led-upboard driver.
> + */

...

> +	int blue_gpio = -1, yellow_gpio = -1, green_gpio = -1, red_gpio = -1;

NAK.

...

> +		blue_gpio = desc_to_gpio(desc);

NAK.

...

> +		yellow_gpio = desc_to_gpio(desc);

NAK.

...

> +		green_gpio = desc_to_gpio(desc);

NAK.

...

> +		red_gpio = desc_to_gpio(desc);

NAK.

...

> +/*
> + *	Refer https://www.kernel.org/doc/htmldocs/writing_musb_glue_layer/device-platform-data.html,
> + *	the id field could be set to -1 (equivalent to PLATFORM_DEVID_NONE),
> + *  -2 (equivalent to PLATFORM_DEVID_AUTO) or start with 0 for the first
> + *	device of this kind if we want a specific id number.
> + */

Useless comment. Just use the proper definition.

> +	if (devm_mfd_add_devices(fpga->dev, 0,
> +				 upboard_gpio_led_cells,
> +				 ARRAY_SIZE(upboard_gpio_led_cells),
> +				 NULL, 0, NULL)) {
> +		dev_info(fpga->dev, "Failed to add GPIO leds");
> +	}


	ret = ...(...);
	if (ret)
		dev_warn(...);


...

> +	/* get fpga/EC protocol hardware version */
> +	acpi_evaluate_integer(ACPI_HANDLE(&pdev->dev), "_HRV", NULL, &hrv);

No error check?

...

> +	system_id = dmi_first_match(upboard_dmi_table);
> +	if (system_id)
> +		quirks = (unsigned long)system_id->driver_data;
> +
> +	if (hrv == UPFPGA_PROTOCOL_V1_HRV &&
> +	    (quirks & UPFPGA_QUIRK_HRV1_IS_PROTO2))
> +		hrv = UPFPGA_PROTOCOL_V2_HRV;

Maybe it's easier to provide driver data?

...

> +	fpga_data = (const struct upboard_fpga_data *) id->driver_data;


Use device_get_match_data().

...

> +	if (quirks & UPFPGA_QUIRK_UNINITIALISED) {
> +		dev_info(&pdev->dev, "FPGA not initialised by this BIOS");

dev_warn()?

> +		fpga->uninitialised = true;
> +	}

...

> +	dev_set_drvdata(&pdev->dev, fpga);

platform_set_drvdata().

> +	fpga->dev = &pdev->dev;
> +	fpga->regmap = devm_regmap_init(&pdev->dev, NULL,
> +					fpga, fpga_data->regmapconf);

Can be one line and you can actually have

	struct device *dev = &pdev->dev;

at the top of the function.

> +	fpga->regmapconf = fpga_data->regmapconf;

Why is it done if you know that error might happen?

> +	if (IS_ERR(fpga->regmap))
> +		return PTR_ERR(fpga->regmap);

...

> +	/* gpio leds initialize */

GPIO LEDs

...

> +		ret =  devm_mfd_add_devices(&pdev->dev, 0,

Use proper definition.

> +					    upboard_gpio_led_cells,
> +					    ARRAY_SIZE(upboard_gpio_led_cells),
> +					    NULL, 0, NULL);

> +			dev_err(&pdev->dev, "Failed to add GPIO leds");
> +			return ret;

	return dev_err_probe();

> +		}
> +	}

+ blank line.

> +	return devm_mfd_add_devices(&pdev->dev, 0,

Use proper definition.

> +				    fpga_data->cells,
> +				    fpga_data->ncells,
> +				    NULL, 0, NULL);
> +}

...

Move ACPI ID table here, it's not needed to have it upper.

> +static struct platform_driver upboard_fpga_driver = {
> +	.driver = {
> +		.name = "upboard-fpga",
> +		.acpi_match_table = upboard_fpga_acpi_match,
> +	},
> +};

...

The header file missing several forward declarations and inclusions, like

#include <linux/types.h>

struct regmap;

-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC 2/3] pinctrl: Add support pin control for UP board CPLD/FPGA
  2022-12-07 16:33 ` [RFC 2/3] pinctrl: Add support pin control " larry.lai
@ 2022-12-07 21:32   ` Andy Shevchenko
  2023-04-25 15:28     ` 回覆: " Larry Lai
  0 siblings, 1 reply; 12+ messages in thread
From: Andy Shevchenko @ 2022-12-07 21:32 UTC (permalink / raw)
  To: larry.lai
  Cc: lee, linus.walleij, pavel, linux-kernel, linux-gpio, linux-leds,
	GaryWang, musa.lin, jack.chang, noah.hung

On Thu, Dec 08, 2022 at 12:33:58AM +0800, larry.lai wrote:
> The UP Squared board <http://www.upboard.com> implements certain
> features (pin control) through an on-board FPGA.

(I already reviewed previous version and you have my tag, but some new comments
 which may be already answered, sorry for the repetition in such cases).

Actually... based on the below comments I have to withdraw my tag.
This driver may not go into upstream in the current form.

I'm puzzled if it's indeed the code I have reviewed?

...

> +#include <linux/acpi.h>

This is actually not needed, but property.h.
See below how.

...

> +static int upboard_fpga_request_enable(struct pinctrl_dev *pctldev,
> +				       struct pinctrl_gpio_range *range,
> +				       unsigned int pin)
> +{
> +	const struct pin_desc * const pd = pin_desc_get(pctldev, pin);
> +	const struct upboard_pin *p;
> +	int ret;

> +	if (!pd)
> +		return -EINVAL;

When this can be true?
Ditto for other functions with this conditional.

> +	p = pd->drv_data;
> +	if (p->funcbit) {
> +		ret = regmap_field_write(p->funcbit, 0);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	if (p->enbit) {
> +		ret = regmap_field_write(p->enbit, 1);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return 0;
> +};

...

> +	struct upboard_pinctrl *pctrl = container_of(gc, struct upboard_pinctrl, chip);

Don't you set a private data of GPIO chip to point to your custom structure?

Either way, I would recommend to make gpio_chip a first member in your
upboard_pinctrl.

...

> +			int mode = 1;

See below about default.

> +			unsigned int val;
> +
> +			switch (pctrl->ident) {
> +			case 15:
> +				mode = 2;
> +			break;
> +			}

Wrong indentation, missing default.
Same for the rest below.

> +		} else if (strstr(pctrl->pctldesc->pins[offset[i]].name,
> +				  "UART")) {

One line would be better to read, same for the rest below.

...

> +		/* input pins */
> +		if (strstr(pctrl->pctldesc->pins[offset[i]].name, "RX"))
> +			input = true;
> +		if (strstr(pctrl->pctldesc->pins[offset[i]].name, "CTS"))
> +			input = true;
> +		if (strstr(pctrl->pctldesc->pins[offset[i]].name, "ADC"))
> +			input = true;
> +		if (strstr(pctrl->pctldesc->pins[offset[i]].name, "MISO"))
> +			input = true;
> +		if (strstr(pctrl->pctldesc->pins[offset[i]].name, "DIN"))
> +			input = true;

Can you have this in some static const data structure that is associated with
pin list?

...

> +static void upboard_gpio_free(struct gpio_chip *gc, unsigned int offset)
> +{
> +	struct upboard_pinctrl *pctrl = container_of(gc, struct upboard_pinctrl, chip);
> +	int gpio = upboard_rpi_to_native_gpio(gc, offset);
> +	unsigned int pin = pctrl->rpi_mapping[offset];
> +	char name[strlen(pctrl->pctldesc->pins[pin].name)];
> +	char *p;

> +	if (gpio < 0)
> +		return;

When this can be true?
Same to the rest below.

> +	pinctrl_gpio_free(gpio);
> +
> +	strcpy(name, pctrl->pctldesc->pins[pin].name);
> +	p = name;
> +	upboard_alt_func_enable(gc, strsep(&p, "_"));
> +}

...

> +	/* APL03 open drain GPIO */
> +	if (pctrl->ident == 9) {
> +		if (pin == 0 || pin == 1) {
> +			int val = readl(pctrl->pins[pin].regs);
> +
> +			if (value)
> +				val |= PADCFG0_GPIOTXDIS;
> +			else
> +				val &= ~PADCFG0_GPIOTXDIS;
> +			writel(val, pctrl->pins[pin].regs);

Huh?!

If we need OD support in the pinctrl-intel, add it there.

> +		}

...

> +	if (strstr(dev_name(&pdev->dev), "INTC1055:00") ||
> +	    strstr(dev_name(&pdev->dev), "INT34C5:00")) {
> +		struct intel_pinctrl *intel_pctrl = gpiochip_get_data(gc);
> +		struct pinctrl_dev *pctldev;
> +		struct pinctrl_gpio_range *range;
> +
> +		pctldev = intel_pctrl->pctldev;
> +		if (pctldev == NULL)
> +			return NULL;
> +
> +		range = pinctrl_find_gpio_range_from_pin(pctldev, pin);
> +		if (range)
> +			pin = pin - range->pin_base;
> +		else
> +			return NULL;
> +
> +		if (range->pin_base < 67)
> +			res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +		else if (range->pin_base < 171)
> +			res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> +		else if (range->pin_base < 260)
> +			res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
> +		else
> +			res = platform_get_resource(pdev, IORESOURCE_MEM, 3);
> +	}
> +
> +	if (IS_ERR(res)) {
> +		dev_err(gc->parent, "upboard resource get failed");
> +		return NULL;
> +	}

> +	base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
> +	offset = readl(base + PADBAR);
> +	value = readl(base + REVID);
> +
> +	if (((value & REVID_MASK) >> REVID_SHIFT) >= 0x94)
> +		nregs = 4;
> +	else
> +		nregs = 2;
> +
> +	return base + offset + reg + pin * nregs * 4;
> +}

What the heck is this?!
Was it really in the version I have reviewed before?

...

> +static void upboard_irq_ack(struct irq_data *d)
> +{
> +	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
> +
> +	dev_info(NULL, "upboard_irq_ack");

Besides dev_info() and strange NULL, why do you need these messages? What for?
Same to the rest below.

> +}

...

> +	data->parent_data = irq_get_irq_data(pctrl->pins[offset].irq);
> +	if (data->parent_data) {
> +		dev_info(NULL, "%s: no NULL", __func__);

What?!

> +		return irq_chip_set_type_parent(data, type);
> +	}
> +
> +	dev_info(NULL, "%s: NULL", __func__);

Ditto.

...

> +static struct irq_chip upboard_gpio_irqchip = {
> +	.name = "upboard-gpio-irq",
> +	.irq_ack = upboard_irq_ack,
> +	.irq_mask = upboard_irq_mask,
> +	.irq_unmask = upboard_irq_unmask,
> +	.irq_set_type = upboard_irq_chip_set_type,
> +	.flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE,

It should be set IMMUTABLE.

> +};

...

> +	hid = acpi_device_hid(adev);
> +	if (!strcmp(hid, "AANT0F00") || !strcmp(hid, "AANT0F04") ||
> +	    !strcmp(hid, "AANT0000")) {

This is done via ACPI ID table (but without announcing it as module supported
hardware) and corresponding match functions.

> +		pctldesc = &upboard_up_pinctrl_desc;
> +		rpi_mapping = upboard_up_rpi_mapping;
> +		ngpio  = ARRAY_SIZE(upboard_up_rpi_mapping);
> +	} else if (!strcmp(hid, "AANT0F01")) {
> +		pctldesc = &upboard_up2_pinctrl_desc;
> +		rpi_mapping = upboard_up2_rpi_mapping;
> +		ngpio  = ARRAY_SIZE(upboard_up2_rpi_mapping);
> +	} else if (!strcmp(hid, "AANT0F02")) {
> +		pctldesc = &upboard_upcore_crex_pinctrl_desc;
> +		rpi_mapping = upboard_upcore_crex_rpi_mapping;
> +		ngpio  = ARRAY_SIZE(upboard_upcore_crex_rpi_mapping);
> +	} else if (!strcmp(hid, "AANT0F03")) {
> +		pctldesc = &upboard_upcore_crst02_pinctrl_desc;
> +		rpi_mapping = upboard_upcore_crst02_rpi_mapping;
> +		ngpio  = ARRAY_SIZE(upboard_upcore_crst02_rpi_mapping);
> +	} else
> +		return -ENODEV;

...

> +	pins = devm_kzalloc(&pdev->dev,
> +			    sizeof(*pins) * pctldesc->npins,
> +			    GFP_KERNEL);

devm_kcalloc() or even devm_kmalloc_array().

> +	if (!pins)
> +		return -ENOMEM;

...

I stopped here since I don't believe that I have given a tag to this earlier...
If it's the case, I don't know what I have got that day.

-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC 0/3] Add support control UP board CPLD/FPGA pin control
  2022-12-07 21:09 ` [RFC 0/3] Add support control UP board CPLD/FPGA pin control Linus Walleij
@ 2022-12-07 21:33   ` Andy Shevchenko
  0 siblings, 0 replies; 12+ messages in thread
From: Andy Shevchenko @ 2022-12-07 21:33 UTC (permalink / raw)
  To: Linus Walleij
  Cc: larry.lai, lee, pavel, linux-kernel, linux-gpio, linux-leds,
	GaryWang, musa.lin, jack.chang, noah.hung

On Wed, Dec 07, 2022 at 10:09:35PM +0100, Linus Walleij wrote:
> On Wed, Dec 7, 2022 at 5:36 PM larry.lai <larry.lai@yunjingtech.com> wrote:
> 
> > The UP board <http://www.upboard.com> is the computer board for
> > Professional Makers and Industrial Applications. We want to upstream
> > the UP board 40-pin GP-bus Kernel driver for giving the users better
> > experience on the software release. (not just download from UP board
> > github)
> >
> > These patches are generated from the Linux kernel mainline tag v6.0.
> 
> Why are these patches tagged RFC now? Weird.

I can easily tell you, this code is no go, too many problematic places.

-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC 0/3] Add support control UP board CPLD/FPGA pin control
  2022-12-07 16:33 [RFC 0/3] Add support control UP board CPLD/FPGA pin control larry.lai
                   ` (3 preceding siblings ...)
  2022-12-07 21:09 ` [RFC 0/3] Add support control UP board CPLD/FPGA pin control Linus Walleij
@ 2022-12-07 21:36 ` Andy Shevchenko
  2022-12-07 21:38   ` Andy Shevchenko
  4 siblings, 1 reply; 12+ messages in thread
From: Andy Shevchenko @ 2022-12-07 21:36 UTC (permalink / raw)
  To: larry.lai
  Cc: lee, linus.walleij, pavel, linux-kernel, linux-gpio, linux-leds,
	GaryWang, musa.lin, jack.chang, noah.hung

On Thu, Dec 08, 2022 at 12:33:56AM +0800, larry.lai wrote:
> The UP board <http://www.upboard.com> is the computer board for 
> Professional Makers and Industrial Applications. We want to upstream 
> the UP board 40-pin GP-bus Kernel driver for giving the users better 
> experience on the software release. (not just download from UP board 
> github)
> 
> These patches are generated from the Linux kernel mainline tag v6.0.

I have just checked the v3 (previous version of this) and I haven't found any
evidence that I gave my tag or permission to use it. What the heck is going on
here?!

-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC 0/3] Add support control UP board CPLD/FPGA pin control
  2022-12-07 21:36 ` Andy Shevchenko
@ 2022-12-07 21:38   ` Andy Shevchenko
  0 siblings, 0 replies; 12+ messages in thread
From: Andy Shevchenko @ 2022-12-07 21:38 UTC (permalink / raw)
  To: larry.lai
  Cc: lee, linus.walleij, pavel, linux-kernel, linux-gpio, linux-leds,
	GaryWang, musa.lin, jack.chang, noah.hung

On Wed, Dec 07, 2022 at 11:36:37PM +0200, Andy Shevchenko wrote:
> On Thu, Dec 08, 2022 at 12:33:56AM +0800, larry.lai wrote:
> > The UP board <http://www.upboard.com> is the computer board for 
> > Professional Makers and Industrial Applications. We want to upstream 
> > the UP board 40-pin GP-bus Kernel driver for giving the users better 
> > experience on the software release. (not just download from UP board 
> > github)
> > 
> > These patches are generated from the Linux kernel mainline tag v6.0.
> 
> I have just checked the v3 (previous version of this) and I haven't found any
> evidence that I gave my tag or permission to use it. What the heck is going on
> here?!

NAK to this series.

-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply	[flat|nested] 12+ messages in thread

* 回覆: [RFC 1/3] mfd: Add support for UP board CPLD/FPGA
  2022-12-07 21:10   ` Andy Shevchenko
@ 2023-04-25 15:28     ` Larry Lai
  0 siblings, 0 replies; 12+ messages in thread
From: Larry Lai @ 2023-04-25 15:28 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: lee, linus.walleij, pavel, linux-kernel, linux-gpio, linux-leds,
	GaryWang, Musa Lin, Jack Chang, Noah Hung, Michael Wang

Dear Andy, 
 
        Thank you for spending time to review this code, please kindly check the following comments with “>>>” beginning.
        Some of issues we will fix in new RFC_230426, some may need you give us more examples or comments.
 
Best Regards,
Larry Lai
 
寄件者: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
日期: 星期四, 2022年12月8日 上午5:10
收件者: Larry Lai <larry.lai@yunjingtech.com>
副本: lee@kernel.org <lee@kernel.org>, linus.walleij@linaro.org <linus.walleij@linaro.org>, pavel@ucw.cz <pavel@ucw.cz>, linux-kernel@vger.kernel.org <linux-kernel@vger.kernel.org>, linux-gpio@vger.kernel.org <linux-gpio@vger.kernel.org>, linux-leds@vger.kernel.org <linux-leds@vger.kernel.org>, GaryWang@aaeon.com.tw <GaryWang@aaeon.com.tw>, Musa Lin <musa.lin@yunjingtech.com>, Jack Chang <jack.chang@yunjingtech.com>, Noah Hung <noah.hung@yunjingtech.com>, Javier Arteaga <javier@emutex.com>, Nicola Lunghi <nicola.lunghi@emutex.com>
主旨: Re: [RFC 1/3] mfd: Add support for UP board CPLD/FPGA
On Thu, Dec 08, 2022 at 12:33:57AM +0800, larry.lai wrote:
> The UP Squared board <http://www.upboard.com> implements certain
> features (pin control, onboard LEDs or CEC) through an on-board CPLD/FPGA.
> 
> This mfd driver implements the line protocol to read and write registers
> from the FPGA through regmap. The register address map is also included.
> 
> The UP Boards provide a few I/O pin headers (for both GPIO and
> functions), including a 40-pin Raspberry Pi compatible header.
> 
> This patch implements support for the FPGA-based pin controller that
> manages direction and enable state for those header pins.
> 
> Partial support UP boards:
> * UP core + CREX
> * UP core + CRST02

...

> +#include <linux/acpi.h>
> +#include <linux/dmi.h>

> +#include <linux/gpio.h>

I'm not sure if you read my previous emails regarding the topic.
This header must not be in the new code.
>>> fixed.

> +#include <linux/kernel.h>
> +#include <linux/leds.h>
> +#include <linux/mfd/core.h>
> +#include <linux/mfd/upboard-fpga.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>


Missing bits.h and err.h at least.
>>> fixed.
...

> +struct upboard_fpga_data {
> +     const struct regmap_config *regmapconf;

No need to repeat regmap twice.
>>> fixed.

> +     const struct mfd_cell *cells;
> +     size_t ncells;
> +};

...

> +#define MENUFACTURER_ID_MASK         0xFF

GENMASK()?
>>> fixed.
...

> +#define FIRMWARE_ID_MASK             0xF

Ditto.
>>> fixed.
...

> +/* Apollo Lake GPIO pin number mapping to FPGA LED */
> +#define APL_GPIO_218                 507

No way. It should be addressed as GPIO chip reference and relative pin
(or GPIO, whichever suits better for your purposes) number.
>>> This section has been removed in new RFC patch, please ignore it.
...

> +/* For UP Board Series FPGA register read/write protocols                   */
> +/* EMUTEX specs:                                                            */
> +/* D0   D1  D2  D3  D4  D5  D6  D7  D8  D9 .... D22  D23                      */
> +/* [RW][        address           ][     DATA        ]                     */
> +
> +/* Read Sequence:                                                             */
> +/*      ___   ____________________________________________________   _________*/
> +/* clr:    \_/ <--low-pulse does start the write-readback         \_/<--start */
> +/*             sequence with partital reset of internal          new sequence*/
> +/*             registers but the CONF-REG.                                     */
> +/*        ____________________________________________________________________*/
> +/* rst: _/       _   _   _        _   _   _   __       __   __   _            */
> +/* stb: STB#1->_/1\_/2\_/3\_...._/7\_/8\_/9\_/10\_..../23\_/24\_/<-STB#25 edge*/
> +/*                                                              is needed  */
> +/*                                                                to ACK     */
> +/*             (D0 - D7 stb rising latch)                                     */
> +/* data_in:     D0  D1  D2  .... D6  D7  don't ........ care(DC)              */
> +/* data_out:    don't ...........care(DC)  D8   D9 ....  D22  D23           */
> +/*                                     (D8 - D23 stb falling latch)          */
> +/* flag_Read:                                  _________...._________              */
> +/*      __DC_   ____________...._________/                      \_            */
> +/* counter:                                                                */
> +/*    [00]DC[00][01][02] ............[08][9][10]............[24][00]       */
> +/* CONF-REG:                                                               */
> +/*    [00] [                         CONF-REG               ]              */
> +/* wreg:                                                                    */
> +/*    [00]DC[00][  wreg=SHFT(wreg)  ][ADR][DATA][wreg=SHFT(wreg]           */
> +/* wreg2:                                                                    */
> +/*                                              [         (COPY)=ADDR ]       */

This has too many /* */ and TABs vs space mix... Please, fix it.
>>> fixed.
Is it SPI 24-bit bit-banging? Why spi-gpio can't be utilized for it?
>>> The UP board FPGA protocol (stb, clr, rst, in, out) is different with SPI 24-bit banging (CS, CLK, MOST, MOSO).
...

> +/* Write Sequence:                                                          */
> +/*      ___   ____________________________________________________   _________*/
> +/* clr:    \_/ <--low-pulse does start the write-readback         \_/<--start */
> +/*             sequence with partital reset of internal          new sequence*/
> +/*             registers but the CONF-REG.                                     */
> +/*        ____________________________________________________________________*/
> +/* rst: _/       _   _   _        _   _   _   __       __   __   _            */
> +/* stb: STB#1->_/1\_/2\_/3\_...._/7\_/8\_/9\_/10\_..../23\_/24\_/<-STB#25 edge*/
> +/*                                                              is needed  */
> +/*                                                              to ACK     */
> +/*             (D0 - D23 stb rising latch)                                    */
> +/* data_in:     D0  D1  D2  .... D6  D7  D8  D9 ....  D22  D23                */
> +/* data_out:    don't ................................care (DC)               */
> +/* flag_Read:                                                                */
> +/*      __DC_   ____________....__________________________________            */
> +/* counter:                                                                */
> +/*    [00]DC[00][01][02] ............[08][9][10]............[24][00]          */
> +/* wreg:                                                                   */
> +/*    [00]DC[00][wreg=SHFT(wreg)&dat_in ][SHFT(wreg)&dat_in][DAT]             */
> +/* wreg2:                                                                  */
> +/*                                             [     (COPY)=ADDR     ]       */
> +/* CONF-REG:                                                               */
> +/*    [00] [      CONF-REG = OLD VALUE                       ][CONF-REG=DAT]*/


Same comments as per above.
>>> fixed.
...

> +             gpiod_set_value(fpga->datain_gpio, (reg >> i) & 0x1);

!!(reg & BIT(i))
>>> fixed.
...

> +             gpiod_set_value(fpga->datain_gpio, (val >> i) & 0x1);

Ditto.
>>> fixed.
But see above.

...

> +static struct gpio_led upboard_gpio_leds[] = {
> +     {
> +             .name = "upboard:blue:",
> +             .gpio = APL_GPIO_218,

You must understand that it won't work with dynamic GPIO bases which will be
enabled in v6.2-rc1. And even in general it must not be like this.
>>> fixed.

> +             .default_state = LEDS_GPIO_DEFSTATE_KEEP,
> +     },
> +};

...

> +     enum gpiod_flags flags;
> +
> +     flags = fpga->uninitialised ? GPIOD_OUT_LOW : GPIOD_ASIS;

Can be united.
>>> fixed.
...

> +     /*
> +      * The SoC pinctrl driver may not support reserving the GPIO line for
> +      * FPGA reset without causing an undesired reset pulse. This will clear
> +      * any settings on the FPGA, so only do it if we must.
> +      * Reset gpio defaults HIGH, get gpio and set to LOW, then set back to
> +      * HIGH as a pulse.
> +      */
> +     if (fpga->uninitialised) {
> +             fpga->reset_gpio = devm_gpiod_get(fpga->dev, "reset", GPIOD_OUT_LOW);
> +             if (IS_ERR(fpga->reset_gpio))
> +                     return PTR_ERR(fpga->reset_gpio);

No sleep for the hardware to be really reset?
>>> This is hardware reset pin, so there's no need the sleep wait.

> +             gpiod_set_value(fpga->reset_gpio, 1);
> +     }

> +/*
> + * MFD upboard-fpga is acpi driver and can recognize the AANT ID from different

ACPI
>>> fixed.
> + * kind of upboards. We get the led gpio initialized information from this

LED GPIO
>>> fixed.
> + * then add led-upboard driver.
> + */

...

> +     int blue_gpio = -1, yellow_gpio = -1, green_gpio = -1, red_gpio = -1;

NAK.
>>> fixed.
...

> +             blue_gpio = desc_to_gpio(desc);

NAK.
>>> fixed.
...

> +             yellow_gpio = desc_to_gpio(desc);

NAK.
>>> fixed.
...

> +             green_gpio = desc_to_gpio(desc);

NAK.
>>> fixed.
...

> +             red_gpio = desc_to_gpio(desc);

NAK.
>>> fixed.
...

> +/*
> + *   Refer https://www.kernel.org/doc/htmldocs/writing_musb_glue_layer/device-platform-data.html,
> + *   the id field could be set to -1 (equivalent to PLATFORM_DEVID_NONE),
> + *  -2 (equivalent to PLATFORM_DEVID_AUTO) or start with 0 for the first
> + *   device of this kind if we want a specific id number.
> + */

Useless comment. Just use the proper definition.
>>> remove useless comment and use PLATFORM_DEVID_AUTO proper definition.

> +     if (devm_mfd_add_devices(fpga->dev, 0,
> +                              upboard_gpio_led_cells,
> +                              ARRAY_SIZE(upboard_gpio_led_cells),
> +                              NULL, 0, NULL)) {
> +             dev_info(fpga->dev, "Failed to add GPIO leds");
> +     }


        ret = ...(...);
        if (ret)
                dev_warn(...);
>>> fixed.

...

> +     /* get fpga/EC protocol hardware version */
> +     acpi_evaluate_integer(ACPI_HANDLE(&pdev->dev), "_HRV", NULL, &hrv);

No error check?
>>> This section has been removed in new RFC patch, please ignore it.
...

> +     system_id = dmi_first_match(upboard_dmi_table);
> +     if (system_id)
> +             quirks = (unsigned long)system_id->driver_data;
> +
> +     if (hrv == UPFPGA_PROTOCOL_V1_HRV &&
> +         (quirks & UPFPGA_QUIRK_HRV1_IS_PROTO2))
> +             hrv = UPFPGA_PROTOCOL_V2_HRV;

Maybe it's easier to provide driver data?
>>> This section has been removed in new RFC patch, please ignore it.
...

> +     fpga_data = (const struct upboard_fpga_data *) id->driver_data;


Use device_get_match_data().
>>> This section has been removed in new RFC patch, please ignore it.
...

> +     if (quirks & UPFPGA_QUIRK_UNINITIALISED) {
> +             dev_info(&pdev->dev, "FPGA not initialised by this BIOS");

dev_warn()?
>>> This section has been removed in new RFC patch, please ignore it.
> +             fpga->uninitialised = true;
> +     }

...

> +     dev_set_drvdata(&pdev->dev, fpga);

platform_set_drvdata().
>>> fixed.
> +     fpga->dev = &pdev->dev;
> +     fpga->regmap = devm_regmap_init(&pdev->dev, NULL,
> +                                     fpga, fpga_data->regmapconf);

Can be one line and you can actually have

        struct device *dev = &pdev->dev;

at the top of the function.
>>> fixed.
> +     fpga->regmapconf = fpga_data->regmapconf;

Why is it done if you know that error might happen?
>>> This section has been removed in new RFC patch, please ignore it.
> +     if (IS_ERR(fpga->regmap))
> +             return PTR_ERR(fpga->regmap);

...

> +     /* gpio leds initialize */

GPIO LEDs
>>> fixed.
...

> +             ret =  devm_mfd_add_devices(&pdev->dev, 0,

Use proper definition.
>>> This section has been removed in new RFC patch, please ignore it.
> +                                         upboard_gpio_led_cells,
> +                                         ARRAY_SIZE(upboard_gpio_led_cells),
> +                                         NULL, 0, NULL);

> +                     dev_err(&pdev->dev, "Failed to add GPIO leds");
> +                     return ret;

        return dev_err_probe();
>>> This section has been removed in new RFC patch, please ignore it.
> +             }
> +     }

+ blank line.
>>> fixed.
> +     return devm_mfd_add_devices(&pdev->dev, 0,

Use proper definition.
>>> fixed by using PLATFORM_DEVID_AUTO definition.
> +                                 fpga_data->cells,
> +                                 fpga_data->ncells,
> +                                 NULL, 0, NULL);
> +}

...

Move ACPI ID table here, it's not needed to have it upper.
>>> Could you kindly explain more and give some examples?
> +static struct platform_driver upboard_fpga_driver = {
> +     .driver = {
> +             .name = "upboard-fpga",
> +             .acpi_match_table = upboard_fpga_acpi_match,
> +     },
> +};

...

The header file missing several forward declarations and inclusions, like
>>> fixed.
#include <linux/types.h>

struct regmap;

-- 
With Best Regards,
Andy Shevchenko

^ permalink raw reply	[flat|nested] 12+ messages in thread

* 回覆: [RFC 2/3] pinctrl: Add support pin control for UP board CPLD/FPGA
  2022-12-07 21:32   ` Andy Shevchenko
@ 2023-04-25 15:28     ` Larry Lai
  0 siblings, 0 replies; 12+ messages in thread
From: Larry Lai @ 2023-04-25 15:28 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: lee, linus.walleij, pavel, linux-kernel, linux-gpio, linux-leds,
	GaryWang, Musa Lin, Jack Chang, Noah Hung, Michael Wang

Dear Andy, 
 
        Thank you for spending time to review this code before, we already fixed some of the issues, please kindly check the following comments with “>>>” beginning.
        Some of issues we will fix in new RFC_230425 patch, some may need you give us more examples or comments, thanks again !
 
Best Regards,
Larry Lai
 
寄件者: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
日期: 星期四, 2022年12月8日 上午5:32
收件者: Larry Lai <larry.lai@yunjingtech.com>
副本: lee@kernel.org <lee@kernel.org>, linus.walleij@linaro.org <linus.walleij@linaro.org>, pavel@ucw.cz <pavel@ucw.cz>, linux-kernel@vger.kernel.org <linux-kernel@vger.kernel.org>, linux-gpio@vger.kernel.org <linux-gpio@vger.kernel.org>, linux-leds@vger.kernel.org <linux-leds@vger.kernel.org>, GaryWang@aaeon.com.tw <GaryWang@aaeon.com.tw>, Musa Lin <musa.lin@yunjingtech.com>, Jack Chang <jack.chang@yunjingtech.com>, Noah Hung <noah.hung@yunjingtech.com>
主旨: Re: [RFC 2/3] pinctrl: Add support pin control for UP board CPLD/FPGA
On Thu, Dec 08, 2022 at 12:33:58AM +0800, larry.lai wrote:
> The UP Squared board <http://www.upboard.com> implements certain
> features (pin control) through an on-board FPGA.

(I already reviewed previous version and you have my tag, but some new comments
 which may be already answered, sorry for the repetition in such cases).

Actually... based on the below comments I have to withdraw my tag.
This driver may not go into upstream in the current form.

I'm puzzled if it's indeed the code I have reviewed?

...

> +#include <linux/acpi.h>

This is actually not needed, but property.h.
See below how.
>>> This pinctrl driver read HID from (acpi_devices) mfd acpi driver, so will need to use some of acpi.h api.
...

> +static int upboard_fpga_request_enable(struct pinctrl_dev *pctldev,
> +                                    struct pinctrl_gpio_range *range,
> +                                    unsigned int pin)
> +{
> +     const struct pin_desc * const pd = pin_desc_get(pctldev, pin);
> +     const struct upboard_pin *p;
> +     int ret;

> +     if (!pd)
> +             return -EINVAL;

When this can be true?
Ditto for other functions with this conditional.
>>> Confirm this will not be true, so remove this code in new patch, please ignore it.
> +     p = pd->drv_data;
> +     if (p->funcbit) {
> +             ret = regmap_field_write(p->funcbit, 0);
> +             if (ret)
> +                     return ret;
> +     }
> +
> +     if (p->enbit) {
> +             ret = regmap_field_write(p->enbit, 1);
> +             if (ret)
> +                     return ret;
> +     }
> +
> +     return 0;
> +};

...

> +     struct upboard_pinctrl *pctrl = container_of(gc, struct upboard_pinctrl, chip);

Don't you set a private data of GPIO chip to point to your custom structure?
>>> We give the gpio chip data in the pinctrl probe function to distinguish the different board HID to instead use private data of GPIO chip. This can determine which board used in every function.

Either way, I would recommend to make gpio_chip a first member in your
upboard_pinctrl.
>>> fixed.
...

> +                     int mode = 1;

See below about default.

> +                     unsigned int val;
> +
> +                     switch (pctrl->ident) {
> +                     case 15:
> +                             mode = 2;
> +                     break;
> +                     }

Wrong indentation, missing default.
Same for the rest below.
>>> fixed.

> +             } else if (strstr(pctrl->pctldesc->pins[offset[i]].name,
> +                               "UART")) {

One line would be better to read, same for the rest below.
>>> fixed.
...

> +             /* input pins */
> +             if (strstr(pctrl->pctldesc->pins[offset[i]].name, "RX"))
> +                     input = true;
> +             if (strstr(pctrl->pctldesc->pins[offset[i]].name, "CTS"))
> +                     input = true;
> +             if (strstr(pctrl->pctldesc->pins[offset[i]].name, "ADC"))
> +                     input = true;
> +             if (strstr(pctrl->pctldesc->pins[offset[i]].name, "MISO"))
> +                     input = true;
> +             if (strstr(pctrl->pctldesc->pins[offset[i]].name, "DIN"))
> +                     input = true;

Can you have this in some static const data structure that is associated with
pin list?
>>> Due to the offsets of boards are different, so we need to scan all boards offset first, so we didn't use the static const data to be associated with.
...

> +static void upboard_gpio_free(struct gpio_chip *gc, unsigned int offset)
> +{
> +     struct upboard_pinctrl *pctrl = container_of(gc, struct upboard_pinctrl, chip);
> +     int gpio = upboard_rpi_to_native_gpio(gc, offset);
> +     unsigned int pin = pctrl->rpi_mapping[offset];
> +     char name[strlen(pctrl->pctldesc->pins[pin].name)];
> +     char *p;

> +     if (gpio < 0)
> +             return;

When this can be true?
Same to the rest below.
>>> Confirm this will not be true, so remove this code in new patch, please ignore it.
> +     pinctrl_gpio_free(gpio);
> +
> +     strcpy(name, pctrl->pctldesc->pins[pin].name);
> +     p = name;
> +     upboard_alt_func_enable(gc, strsep(&p, "_"));
> +}

...

> +     /* APL03 open drain GPIO */
> +     if (pctrl->ident == 9) {
> +             if (pin == 0 || pin == 1) {
> +                     int val = readl(pctrl->pins[pin].regs);
> +
> +                     if (value)
> +                             val |= PADCFG0_GPIOTXDIS;
> +                     else
> +                             val &= ~PADCFG0_GPIOTXDIS;
> +                     writel(val, pctrl->pins[pin].regs);

Huh?!

If we need OD support in the pinctrl-intel, add it there.
>>> Due to this is only for APL03 upboard pin0, pin1 open-drain and this board is already in the wild, so we should not add it to pinctrl-intel to affect others Intel board.
> +             }

...

> +     if (strstr(dev_name(&pdev->dev), "INTC1055:00") ||
> +         strstr(dev_name(&pdev->dev), "INT34C5:00")) {
> +             struct intel_pinctrl *intel_pctrl = gpiochip_get_data(gc);
> +             struct pinctrl_dev *pctldev;
> +             struct pinctrl_gpio_range *range;
> +
> +             pctldev = intel_pctrl->pctldev;
> +             if (pctldev == NULL)
> +                     return NULL;
> +
> +             range = pinctrl_find_gpio_range_from_pin(pctldev, pin);
> +             if (range)
> +                     pin = pin - range->pin_base;
> +             else
> +                     return NULL;
> +
> +             if (range->pin_base < 67)
> +                     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +             else if (range->pin_base < 171)
> +                     res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> +             else if (range->pin_base < 260)
> +                     res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
> +             else
> +                     res = platform_get_resource(pdev, IORESOURCE_MEM, 3);
> +     }
> +
> +     if (IS_ERR(res)) {
> +             dev_err(gc->parent, "upboard resource get failed");
> +             return NULL;
> +     }

> +     base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
> +     offset = readl(base + PADBAR);
> +     value = readl(base + REVID);
> +
> +     if (((value & REVID_MASK) >> REVID_SHIFT) >= 0x94)
> +             nregs = 4;
> +     else
> +             nregs = 2;
> +
> +     return base + offset + reg + pin * nregs * 4;
> +}

What the heck is this?!
Was it really in the version I have reviewed before?
>>> This code is for supporting multi-function pin with runtime change. Do you have any suggestion?
...

> +static void upboard_irq_ack(struct irq_data *d)
> +{
> +     struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
> +
> +     dev_info(NULL, "upboard_irq_ack");

Besides dev_info() and strange NULL, why do you need these messages? What for?
Same to the rest below.
>>> This section has been removed in new RFC patch, please ignore it.
> +}

...

> +     data->parent_data = irq_get_irq_data(pctrl->pins[offset].irq);
> +     if (data->parent_data) {
> +             dev_info(NULL, "%s: no NULL", __func__);

What?!
>>> This section has been removed in new RFC patch, please ignore it.
> +             return irq_chip_set_type_parent(data, type);
> +     }
> +
> +     dev_info(NULL, "%s: NULL", __func__);

Ditto.
>>> This section has been removed in new RFC patch, please ignore it.
...

> +static struct irq_chip upboard_gpio_irqchip = {
> +     .name = "upboard-gpio-irq",
> +     .irq_ack = upboard_irq_ack,
> +     .irq_mask = upboard_irq_mask,
> +     .irq_unmask = upboard_irq_unmask,
> +     .irq_set_type = upboard_irq_chip_set_type,
> +     .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE,

It should be set IMMUTABLE.
>>> This section has been removed in new RFC patch, please ignore it.
> +};

...

> +     hid = acpi_device_hid(adev);
> +     if (!strcmp(hid, "AANT0F00") || !strcmp(hid, "AANT0F04") ||
> +         !strcmp(hid, "AANT0000")) {

This is done via ACPI ID table (but without announcing it as module supported
hardware) and corresponding match functions.
>>> This is for assigning driver data so we need to know HID.

> +             pctldesc = &upboard_up_pinctrl_desc;
> +             rpi_mapping = upboard_up_rpi_mapping;
> +             ngpio  = ARRAY_SIZE(upboard_up_rpi_mapping);
> +     } else if (!strcmp(hid, "AANT0F01")) {
> +             pctldesc = &upboard_up2_pinctrl_desc;
> +             rpi_mapping = upboard_up2_rpi_mapping;
> +             ngpio  = ARRAY_SIZE(upboard_up2_rpi_mapping);
> +     } else if (!strcmp(hid, "AANT0F02")) {
> +             pctldesc = &upboard_upcore_crex_pinctrl_desc;
> +             rpi_mapping = upboard_upcore_crex_rpi_mapping;
> +             ngpio  = ARRAY_SIZE(upboard_upcore_crex_rpi_mapping);
> +     } else if (!strcmp(hid, "AANT0F03")) {
> +             pctldesc = &upboard_upcore_crst02_pinctrl_desc;
> +             rpi_mapping = upboard_upcore_crst02_rpi_mapping;
> +             ngpio  = ARRAY_SIZE(upboard_upcore_crst02_rpi_mapping);
> +     } else
> +             return -ENODEV;

...

> +     pins = devm_kzalloc(&pdev->dev,
> +                         sizeof(*pins) * pctldesc->npins,
> +                         GFP_KERNEL);

devm_kcalloc() or even devm_kmalloc_array().
>>> fixed.
> +     if (!pins)
> +             return -ENOMEM;

...

I stopped here since I don't believe that I have given a tag to this earlier...
If it's the case, I don't know what I have got that day.

-- 
With Best Regards,
Andy Shevchenko

^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2023-04-25 15:29 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-12-07 16:33 [RFC 0/3] Add support control UP board CPLD/FPGA pin control larry.lai
2022-12-07 16:33 ` [RFC 1/3] mfd: Add support for UP board CPLD/FPGA larry.lai
2022-12-07 21:10   ` Andy Shevchenko
2023-04-25 15:28     ` 回覆: " Larry Lai
2022-12-07 16:33 ` [RFC 2/3] pinctrl: Add support pin control " larry.lai
2022-12-07 21:32   ` Andy Shevchenko
2023-04-25 15:28     ` 回覆: " Larry Lai
2022-12-07 16:33 ` [RFC 3/3] leds: Add support for UP board CPLD onboard LEDS larry.lai
2022-12-07 21:09 ` [RFC 0/3] Add support control UP board CPLD/FPGA pin control Linus Walleij
2022-12-07 21:33   ` Andy Shevchenko
2022-12-07 21:36 ` Andy Shevchenko
2022-12-07 21:38   ` Andy Shevchenko

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).