From patchwork Wed Dec 11 16:27:16 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thomas Richard X-Patchwork-Id: 851126 Received: from relay1-d.mail.gandi.net (relay1-d.mail.gandi.net [217.70.183.193]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 86D531DC1B7; Wed, 11 Dec 2024 16:27:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.70.183.193 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1733934482; cv=none; b=BZGXSihIafeXT2Kj0eZij0jwFZjT4W+SqEm27+GWKo86msoGL+FlG7/Kvhi+r7nJCqcX9JuETSp5VoT9D1uFmltWzwqVsCiN5e/uoLsGgDjfi3fQ8S5BIiZwzq76j8NO6oaw0i/PMgmTvKYwiy98chHKJXc8irsQb+FhpxDhLFQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1733934482; c=relaxed/simple; bh=3xjs8JM7xrDB+RKKUbbFq8j8Qmh7RhryeG6sEN7HOSA=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=FsMq3AO5rBMyTZQyjx6lZfXv639Rbbd2R2JxCPpKmEiCcXPc/dV8hVhf8BdLWHkz5CM6irk9D9FEriWuxr+8T0/mt6NYXgKTuwLTYoSi0zHJILBxdJS/tXlCrI48mLXZU4LU/vu46kolDM4bIL7WoG9mmYYI3QMP3YSy6Jem3fU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com; spf=pass smtp.mailfrom=bootlin.com; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b=CxrVOLWN; arc=none smtp.client-ip=217.70.183.193 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=bootlin.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b="CxrVOLWN" Received: by mail.gandi.net (Postfix) with ESMTPSA id 09C8524000B; Wed, 11 Dec 2024 16:27:50 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1733934471; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=jguxTuNFEAQpHZgdLoSpGo9WKxXnlJWcDc+RcR89HLU=; b=CxrVOLWNdlt8jr+l2CygRN2AbnXE05MUbMC/HHRu/Lx6GhLIu3DjU1KpICBekqt0P1Yu4N 8Avei474A4zyjLA1TVnIwKbuDfj3QDoK1RjdFVJE5Mn7sOBTjxiAHZ0kJUFeRtIdlpuwPv a5tj/lWcsEa2+x5M3VywMGIzpLrQN/ijiZuwXtko3XQBHUz5zTWULxfAgUAqqE3dj7xfNA Td9Ur8b9pbq7Ud0vNPajP+/yptutq4qqod56vhEikX7jCVsgPL+6DkH8xpXYSodBYzXiZV ZTiQnJE7tXgSYgpZbVOVD2XBCki8WSuI68EtqP5Tv2OtJlO2xoqOqWAeiNLD7Q== From: Thomas Richard Date: Wed, 11 Dec 2024 17:27:16 +0100 Subject: [PATCH 1/5] mfd: Add support for AAEON UP board FPGA Precedence: bulk X-Mailing-List: linux-gpio@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20241211-aaeon-up-board-pinctrl-support-v1-1-24719be27631@bootlin.com> References: <20241211-aaeon-up-board-pinctrl-support-v1-0-24719be27631@bootlin.com> In-Reply-To: <20241211-aaeon-up-board-pinctrl-support-v1-0-24719be27631@bootlin.com> To: Linus Walleij , Bartosz Golaszewski , Lee Jones , Pavel Machek Cc: linux-gpio@vger.kernel.org, linux-kernel@vger.kernel.org, linux-leds@vger.kernel.org, thomas.petazzoni@bootlin.com, DanieleCleri@aaeon.eu, GaryWang@aaeon.com.tw, Thomas Richard X-Mailer: b4 0.14.1 X-GND-Sasl: thomas.richard@bootlin.com The UP boards implement some features (pin controller, LEDs) through an on-board FPGA. This MFD driver implements the line protocol to communicate with the FPGA through regmap, and registers pin controller and led cells. This commit adds support for UP and UP Squared boards. Based on the work done by Gary Wang . Signed-off-by: Thomas Richard --- drivers/mfd/Kconfig | 12 ++ drivers/mfd/Makefile | 2 + drivers/mfd/upboard-fpga.c | 325 +++++++++++++++++++++++++++++++++++++++ include/linux/mfd/upboard-fpga.h | 55 +++++++ 4 files changed, 394 insertions(+) diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index ae23b317a64e..3fc8ddac68bf 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -2414,5 +2414,17 @@ config MFD_RSMU_SPI Additional drivers must be enabled in order to use the functionality of the device. +config MFD_UPBOARD_FPGA + tristate "Support for the AAeon UP board FPGA" + depends on (X86 && ACPI) + select MFD_CORE + help + Select this option to enable the AAEON UP and UP^2 onboard FPGA. + This is the core driver of this FPGA, which has a pin controller and a + LED controller. + + To compile this driver as a module, choose M here: the module will be + called upboard-fpga. + endmenu endif diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index e057d6d6faef..6ce5bbdf078f 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -290,3 +290,5 @@ obj-$(CONFIG_MFD_ATC260X_I2C) += atc260x-i2c.o obj-$(CONFIG_MFD_RSMU_I2C) += rsmu_i2c.o rsmu_core.o obj-$(CONFIG_MFD_RSMU_SPI) += rsmu_spi.o rsmu_core.o + +obj-$(CONFIG_MFD_UPBOARD_FPGA) += upboard-fpga.o diff --git a/drivers/mfd/upboard-fpga.c b/drivers/mfd/upboard-fpga.c new file mode 100644 index 000000000000..5a330e2f2229 --- /dev/null +++ b/drivers/mfd/upboard-fpga.c @@ -0,0 +1,325 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * UP Board FPGA driver. + * + * FPGA provides more GPIO driving power, LEDS and pin mux function. + * + * Copyright (c) AAEON. All rights reserved. + * Copyright (C) 2024 Bootlin + * + * Author: Gary Wang + * Author: Thomas Richard + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define UPBOARD_AAEON_MANUFACTURER_ID 0x01 +#define UPBOARD_MANUFACTURER_ID_MASK GENMASK(7, 0) + +#define UPBOARD_ADDRESS_SIZE 7 +#define UPBOARD_REGISTER_SIZE 16 + +#define UPBOARD_READ_FLAG BIT(UPBOARD_ADDRESS_SIZE) + +#define UPBOARD_FW_ID_MAJOR_SUPPORTED 0x0 + +#define UPBOARD_FW_ID_BUILD_MASK GENMASK(15, 12) +#define UPBOARD_FW_ID_MAJOR_MASK GENMASK(11, 8) +#define UPBOARD_FW_ID_MINOR_MASK GENMASK(7, 4) +#define UPBOARD_FW_ID_PATCH_MASK GENMASK(3, 0) + +static int upboard_fpga_read(void *context, unsigned int reg, unsigned int *val) +{ + struct upboard_fpga *fpga = context; + int i; + + /* Clear to start new transaction */ + gpiod_set_value(fpga->clear_gpio, 0); + gpiod_set_value(fpga->clear_gpio, 1); + + reg |= UPBOARD_READ_FLAG; + + /* Send clock and addr from strobe & datain pins */ + for (i = UPBOARD_ADDRESS_SIZE; i >= 0; i--) { + gpiod_set_value(fpga->strobe_gpio, 0); + gpiod_set_value(fpga->datain_gpio, !!(reg & BIT(i))); + gpiod_set_value(fpga->strobe_gpio, 1); + } + + gpiod_set_value(fpga->strobe_gpio, 0); + *val = 0; + + /* Read data from dataout pin */ + for (i = UPBOARD_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; +} + +static int upboard_fpga_write(void *context, unsigned int reg, unsigned int val) +{ + struct upboard_fpga *fpga = context; + int i; + + /* Clear to start new transcation */ + gpiod_set_value(fpga->clear_gpio, 0); + gpiod_set_value(fpga->clear_gpio, 1); + + /* Send clock and addr from strobe & datain pins */ + for (i = UPBOARD_ADDRESS_SIZE; i >= 0; i--) { + gpiod_set_value(fpga->strobe_gpio, 0); + gpiod_set_value(fpga->datain_gpio, !!(reg & BIT(i))); + gpiod_set_value(fpga->strobe_gpio, 1); + } + + gpiod_set_value(fpga->strobe_gpio, 0); + + /* Write data to datain pin */ + for (i = UPBOARD_REGISTER_SIZE - 1; i >= 0; i--) { + gpiod_set_value(fpga->datain_gpio, !!(val & BIT(i))); + 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(UPBOARD_REG_PLATFORM_ID, UPBOARD_REG_FIRMWARE_ID), + regmap_reg_range(UPBOARD_REG_FUNC_EN0, UPBOARD_REG_FUNC_EN0), + regmap_reg_range(UPBOARD_REG_GPIO_EN0, UPBOARD_REG_GPIO_EN1), + regmap_reg_range(UPBOARD_REG_GPIO_DIR0, UPBOARD_REG_GPIO_DIR1), +}; + +static const struct regmap_range upboard_up_writable_ranges[] = { + regmap_reg_range(UPBOARD_REG_FUNC_EN0, UPBOARD_REG_FUNC_EN0), + regmap_reg_range(UPBOARD_REG_GPIO_EN0, UPBOARD_REG_GPIO_EN1), + regmap_reg_range(UPBOARD_REG_GPIO_DIR0, UPBOARD_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 = UPBOARD_ADDRESS_SIZE, + .val_bits = UPBOARD_REGISTER_SIZE, + .max_register = UPBOARD_REG_MAX, + .reg_read = upboard_fpga_read, + .reg_write = upboard_fpga_write, + .fast_io = false, + .cache_type = REGCACHE_NONE, + .rd_table = &upboard_up_readable_table, + .wr_table = &upboard_up_writable_table, +}; + +static const struct regmap_range upboard_up2_readable_ranges[] = { + regmap_reg_range(UPBOARD_REG_PLATFORM_ID, UPBOARD_REG_FIRMWARE_ID), + regmap_reg_range(UPBOARD_REG_FUNC_EN0, UPBOARD_REG_FUNC_EN1), + regmap_reg_range(UPBOARD_REG_GPIO_EN0, UPBOARD_REG_GPIO_EN2), + regmap_reg_range(UPBOARD_REG_GPIO_DIR0, UPBOARD_REG_GPIO_DIR2), +}; + +static const struct regmap_range upboard_up2_writable_ranges[] = { + regmap_reg_range(UPBOARD_REG_FUNC_EN0, UPBOARD_REG_FUNC_EN1), + regmap_reg_range(UPBOARD_REG_GPIO_EN0, UPBOARD_REG_GPIO_EN2), + regmap_reg_range(UPBOARD_REG_GPIO_DIR0, UPBOARD_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 = UPBOARD_ADDRESS_SIZE, + .val_bits = UPBOARD_REGISTER_SIZE, + .max_register = UPBOARD_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 const struct mfd_cell upboard_up_mfd_cells[] = { + { .name = "upboard-pinctrl" }, + { .name = "upboard-leds" }, +}; + +static const struct upboard_fpga_data upboard_up_fpga_data = { + .type = UPBOARD_UP_FPGA, + .regmap_config = &upboard_up_regmap_config, +}; + +static const struct upboard_fpga_data upboard_up2_fpga_data = { + .type = UPBOARD_UP2_FPGA, + .regmap_config = &upboard_up2_regmap_config, +}; + +static int upboard_fpga_gpio_init(struct upboard_fpga *fpga) +{ + fpga->enable_gpio = devm_gpiod_get(fpga->dev, "enable", GPIOD_ASIS); + 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); + + gpiod_set_value(fpga->enable_gpio, 1); + + return 0; +} + +static int upboard_fpga_get_firmware_version(struct upboard_fpga *fpga) +{ + unsigned int platform_id, manufacturer_id; + int ret; + + if (!fpga) + return -ENOMEM; + + ret = regmap_read(fpga->regmap, UPBOARD_REG_PLATFORM_ID, &platform_id); + if (ret) + return ret; + + manufacturer_id = platform_id & UPBOARD_MANUFACTURER_ID_MASK; + if (manufacturer_id != UPBOARD_AAEON_MANUFACTURER_ID) + return dev_err_probe(fpga->dev, -ENODEV, + "driver not compatible with custom FPGA FW from manufacturer id %#02x.", + manufacturer_id); + + ret = regmap_read(fpga->regmap, UPBOARD_REG_FIRMWARE_ID, &fpga->firmware_version); + if (ret) + return ret; + + if (FIELD_GET(UPBOARD_FW_ID_MAJOR_MASK, fpga->firmware_version) != + UPBOARD_FW_ID_MAJOR_SUPPORTED) + return dev_err_probe(fpga->dev, -ENODEV, + "unsupported FPGA FW v%lu.%lu.%lu build %#02lx", + FIELD_GET(UPBOARD_FW_ID_MAJOR_MASK, fpga->firmware_version), + FIELD_GET(UPBOARD_FW_ID_MINOR_MASK, fpga->firmware_version), + FIELD_GET(UPBOARD_FW_ID_PATCH_MASK, fpga->firmware_version), + FIELD_GET(UPBOARD_FW_ID_BUILD_MASK, fpga->firmware_version)); + return 0; +} + +static ssize_t upboard_fpga_version_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct upboard_fpga *fpga = dev_get_drvdata(dev); + + return sysfs_emit(buf, "FPGA FW v%lu.%lu.%lu build %#02lx\n", + FIELD_GET(UPBOARD_FW_ID_MAJOR_MASK, fpga->firmware_version), + FIELD_GET(UPBOARD_FW_ID_MINOR_MASK, fpga->firmware_version), + FIELD_GET(UPBOARD_FW_ID_PATCH_MASK, fpga->firmware_version), + FIELD_GET(UPBOARD_FW_ID_BUILD_MASK, fpga->firmware_version)); +} + +static DEVICE_ATTR_RO(upboard_fpga_version); + +static struct attribute *upboard_fpga_attrs[] = { + &dev_attr_upboard_fpga_version.attr, + NULL +}; + +ATTRIBUTE_GROUPS(upboard_fpga); + +static int upboard_fpga_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct upboard_fpga *fpga; + int ret; + + fpga = devm_kzalloc(dev, sizeof(*fpga), GFP_KERNEL); + if (!fpga) + return -ENOMEM; + + fpga->fpga_data = device_get_match_data(dev); + + fpga->dev = dev; + + platform_set_drvdata(pdev, fpga); + + fpga->regmap = devm_regmap_init(dev, NULL, fpga, fpga->fpga_data->regmap_config); + if (IS_ERR(fpga->regmap)) + return PTR_ERR(fpga->regmap); + + ret = upboard_fpga_gpio_init(fpga); + if (ret) + return dev_err_probe(dev, ret, "Failed to initialize FPGA common GPIOs"); + + ret = upboard_fpga_get_firmware_version(fpga); + if (ret) + return ret; + + return devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, upboard_up_mfd_cells, + ARRAY_SIZE(upboard_up_mfd_cells), NULL, 0, NULL); +} + +static const struct acpi_device_id upboard_fpga_acpi_match[] = { + { "AANT0F01", (kernel_ulong_t)&upboard_up2_fpga_data }, + { "AANT0F04", (kernel_ulong_t)&upboard_up_fpga_data }, + {} +}; +MODULE_DEVICE_TABLE(acpi, upboard_fpga_acpi_match); + +static struct platform_driver upboard_fpga_driver = { + .driver = { + .name = "upboard-fpga", + .acpi_match_table = ACPI_PTR(upboard_fpga_acpi_match), + .dev_groups = upboard_fpga_groups, + }, + .probe = upboard_fpga_probe, +}; + +module_platform_driver(upboard_fpga_driver); + +MODULE_AUTHOR("Gary Wang "); +MODULE_AUTHOR("Thomas Richard "); +MODULE_DESCRIPTION("UP Board FPGA driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/upboard-fpga.h b/include/linux/mfd/upboard-fpga.h new file mode 100644 index 000000000000..12231e40f5da --- /dev/null +++ b/include/linux/mfd/upboard-fpga.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * UP Board CPLD/FPGA driver + * + * Copyright (c) AAEON. All rights reserved. + * Copyright (C) 2024 Bootlin + * + * Author: Gary Wang + * Author: Thomas Richard + * + */ + +#ifndef __LINUX_MFD_UPBOARD_FPGA_H +#define __LINUX_MFD_UPBOARD_FPGA_H + +#define UPBOARD_REGISTER_SIZE 16 + +enum upboard_fpgareg { + UPBOARD_REG_PLATFORM_ID = 0x10, + UPBOARD_REG_FIRMWARE_ID = 0x11, + UPBOARD_REG_FUNC_EN0 = 0x20, + UPBOARD_REG_FUNC_EN1 = 0x21, + UPBOARD_REG_GPIO_EN0 = 0x30, + UPBOARD_REG_GPIO_EN1 = 0x31, + UPBOARD_REG_GPIO_EN2 = 0x32, + UPBOARD_REG_GPIO_DIR0 = 0x40, + UPBOARD_REG_GPIO_DIR1 = 0x41, + UPBOARD_REG_GPIO_DIR2 = 0x42, + UPBOARD_REG_MAX, +}; + +enum upboard_fpga_type { + UPBOARD_UP_FPGA, + UPBOARD_UP2_FPGA, +}; + +struct upboard_fpga_data { + enum upboard_fpga_type type; + const struct regmap_config *regmap_config; +}; + +struct upboard_fpga { + struct device *dev; + struct regmap *regmap; + 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; + unsigned int firmware_version; + const struct upboard_fpga_data *fpga_data; +}; + +#endif /* __LINUX_MFD_UPBOARD_FPGA_H */ From patchwork Wed Dec 11 16:27:17 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thomas Richard X-Patchwork-Id: 849444 Received: from relay1-d.mail.gandi.net (relay1-d.mail.gandi.net [217.70.183.193]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 492811DCB09; Wed, 11 Dec 2024 16:27:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.70.183.193 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1733934482; cv=none; b=HKjMxyQ8ag8+rtSXW5s6Dt8l+R/093MSfxq/SzozC3WQIvCm5aS/eFdXKcdSWjrCMPOMhy2XOn/17V43LTQ1cWcb5vlBECRF4EjST77537TRYgr4+dYVTGVrI73pKOP2hTP1C0fv1JbKTxVx6PWIw3Sa9Nd9aei3aH89OaIpKes= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1733934482; c=relaxed/simple; bh=A72m0aIUimExktaT990umlPxE578madL+BQI1rpCTdk=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=sJVyvwLLE09S2AdSruQUFcl+2BI8zXCHe4Upzg5XxXu/FvMQdyjyDIhsZ2iefm9ySMFvxSElj3v2EFNYb1jxCUIXyyDmy+9PDpET6TBO2+nUAN5c/iufAXsg2epgv/wjUXKYdOJXMB7rUFSJokgq1f6qdD9my1D2hENM1ubyU1Q= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com; spf=pass smtp.mailfrom=bootlin.com; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b=kaAV0jIe; arc=none smtp.client-ip=217.70.183.193 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=bootlin.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b="kaAV0jIe" Received: by mail.gandi.net (Postfix) with ESMTPSA id 8650C24000D; Wed, 11 Dec 2024 16:27:51 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1733934472; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=ZQayVAe1OKxHfTylerB8TQVBruMZzFhLn/t/stczNho=; b=kaAV0jIeXmltH5Iy+ZBRkyzAcTGDb2+z5aAzX/Xgmj7amkdiym+9dJDhjaMOSQKHPcUZA8 5jJri6Gu6/+0tX+S5Of22E9vX+vLCTI1OYq8Nzj0CYP5DzQj0rxlEifvrfClCWUbMcXxkk l2ZRb4VEq/BcTw26HN2wXhiQEHbIT5FMl8PuSXejg03Bb080T/3Y1U0K/9qhIYOMPRFdbc FMCCeC3ct8t5kwRxDr8sLZie4Ajul8TRahjUtX9T+HB21aX1i80I8EgGdOr57LHIxgPriM 6Dd2mSrTigMONqtBn9uO1Tb2jVUcFqk4/dQ2B175LZMQoN+zkzIIPpuD21mp7Q== From: Thomas Richard Date: Wed, 11 Dec 2024 17:27:17 +0100 Subject: [PATCH 2/5] leds: Add AAEON UP board LED driver Precedence: bulk X-Mailing-List: linux-gpio@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20241211-aaeon-up-board-pinctrl-support-v1-2-24719be27631@bootlin.com> References: <20241211-aaeon-up-board-pinctrl-support-v1-0-24719be27631@bootlin.com> In-Reply-To: <20241211-aaeon-up-board-pinctrl-support-v1-0-24719be27631@bootlin.com> To: Linus Walleij , Bartosz Golaszewski , Lee Jones , Pavel Machek Cc: linux-gpio@vger.kernel.org, linux-kernel@vger.kernel.org, linux-leds@vger.kernel.org, thomas.petazzoni@bootlin.com, DanieleCleri@aaeon.eu, GaryWang@aaeon.com.tw, Thomas Richard X-Mailer: b4 0.14.1 X-GND-Sasl: thomas.richard@bootlin.com Add support for LEDs on AAEON UP boards. These leds are managed by the onboard FPGA: - UP boards: yellow, green, red - UP Squared boards: blue, yellow, green, red Based on the work done by Gary Wang , largely rewritten. Signed-off-by: Thomas Richard --- drivers/leds/Kconfig | 9 ++++ drivers/leds/Makefile | 1 + drivers/leds/leds-upboard.c | 126 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 136 insertions(+) diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index b784bb74a837..984ef5cfa9fa 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -815,6 +815,15 @@ config LEDS_SC27XX_BLTC This driver can also be built as a module. If so the module will be called leds-sc27xx-bltc. +config LEDS_UPBOARD + tristate "LED support for the UP board" + depends on LEDS_CLASS && MFD_UPBOARD_FPGA + help + This option enables support for the UP board LEDs. + + This driver can also be built as a module. If so the module will be + called leds-upboard. + comment "LED driver for blink(1) USB RGB LED is under Special HID drivers (HID_THINGM)" config LEDS_BLINKM diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 18afbb5a23ee..e7982938ddc1 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -89,6 +89,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..b350eb294280 --- /dev/null +++ b/drivers/leds/leds-upboard.c @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * UP board LED driver. + * + * Copyright (c) AAEON. All rights reserved. + * Copyright (C) 2024 Bootlin + * + * Author: Gary Wang + * Author: Thomas Richard + */ + +#include +#include +#include +#include +#include +#include +#include + +#define led_cdev_to_led_upboard(c) container_of(c, struct upboard_led, cdev) + +struct upboard_led { + struct regmap_field *field; + struct led_classdev cdev; +}; + +struct upboard_led_profile { + const char *name; + unsigned int bit; +}; + +static struct upboard_led_profile upboard_up_led_profile[] = { + { "upboard:yellow:" LED_FUNCTION_STATUS, 0 }, + { "upboard:green:" LED_FUNCTION_STATUS, 1 }, + { "upboard:red:" LED_FUNCTION_STATUS, 2 }, +}; + +static struct upboard_led_profile upboard_up2_led_profile[] = { + { "upboard:blue:" LED_FUNCTION_STATUS, 0 }, + { "upboard:yellow:" LED_FUNCTION_STATUS, 1 }, + { "upboard:green:" LED_FUNCTION_STATUS, 2 }, + { "upboard:red:" LED_FUNCTION_STATUS, 3 }, +}; + +static enum led_brightness upboard_led_brightness_get(struct led_classdev *cdev) +{ + struct upboard_led *led = led_cdev_to_led_upboard(cdev); + int brightness, ret; + + ret = regmap_field_read(led->field, &brightness); + + return ret ? LED_OFF : brightness; +}; + +static int upboard_led_brightness_set(struct led_classdev *cdev, enum led_brightness brightness) +{ + struct upboard_led *led = led_cdev_to_led_upboard(cdev); + + return regmap_field_write(led->field, brightness != LED_OFF); +}; + +static int upboard_led_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct upboard_fpga *fpga = dev_get_drvdata(dev->parent); + struct upboard_led_profile *led_profile; + struct upboard_led *led; + int led_instances, ret, i; + + switch (fpga->fpga_data->type) { + case UPBOARD_UP_FPGA: + led_profile = upboard_up_led_profile; + led_instances = ARRAY_SIZE(upboard_up_led_profile); + break; + case UPBOARD_UP2_FPGA: + led_profile = upboard_up2_led_profile; + led_instances = ARRAY_SIZE(upboard_up2_led_profile); + break; + default: + return dev_err_probe(dev, -EINVAL, "Unknown device type %d\n", + fpga->fpga_data->type); + } + + for (i = 0; i < led_instances; i++) { + const struct reg_field fldconf = { + .reg = UPBOARD_REG_FUNC_EN0, + .lsb = led_profile[i].bit, + .msb = led_profile[i].bit, + }; + + led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL); + if (!led) + return -ENOMEM; + + led->field = devm_regmap_field_alloc(&pdev->dev, 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_blocking = upboard_led_brightness_set; + led->cdev.max_brightness = LED_ON; + + led->cdev.name = led_profile[i].name; + + ret = devm_led_classdev_register(dev, &led->cdev); + if (ret) + return ret; + } + + return 0; +} + +static struct platform_driver upboard_led_driver = { + .driver = { + .name = "upboard-leds", + }, + .probe = upboard_led_probe, +}; + +module_platform_driver(upboard_led_driver); + +MODULE_AUTHOR("Gary Wang "); +MODULE_AUTHOR("Thomas Richard "); +MODULE_DESCRIPTION("UP Board LED driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:upboard-led"); From patchwork Wed Dec 11 16:27:18 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thomas Richard X-Patchwork-Id: 851127 Received: from relay1-d.mail.gandi.net (relay1-d.mail.gandi.net [217.70.183.193]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 493091DD0DC; Wed, 11 Dec 2024 16:27:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.70.183.193 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1733934481; cv=none; b=CvtObbKbYN959zvHbYUwotRajc1iz+7J4bEwIKomSZMm62jWbFRVJuAejIOdpYozTDPsiRdZ9KIkn9LUDHKmGpGnIZWo+UY6b0zAYqSTbypKHD3ZwCOUBuRlnWPbfKnEtbHaMRL1NtNCTSG2s81pYI9G5lB9QWvGkjzyPsg1ptg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1733934481; c=relaxed/simple; bh=R9Ph2LuNSizL9cZ5KwZKjg48Yy4GVSq4AuUp2eZ53Mc=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=hzhLuTxnSGfdT0o61VR2qkrSGxfBQiWppM5qKT3/Yjil+WGNc44q7AKA+ssFbWF7khv5fVxe/Mw9Qlfze6Vqq9iQnMamgVC4XlCL5yq17UhvvGMGFga0gQYK+xjWi3iRxG3MXv1idyq7asNFQRr1aWrfk2VEBEvd0W2cRmDtmUg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com; spf=pass smtp.mailfrom=bootlin.com; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b=jFYOe4Yq; arc=none smtp.client-ip=217.70.183.193 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=bootlin.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b="jFYOe4Yq" Received: by mail.gandi.net (Postfix) with ESMTPSA id 19D3624000C; Wed, 11 Dec 2024 16:27:52 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1733934472; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=GJLX7UutB8ehS0hHXBDQ4WgR2Cmh6ovxEz75/hzjYaI=; b=jFYOe4YqQizwu6An/9RPBSvh2vpj9zksMTEK3eBWi66gZ2Dxo2ZpAAmqDCanz62rpsNjh6 lhS8HWxuaCkcf/rkWCgGfQFcI/hA9CvH/uU+QQ9eICZiuclHKA0EYxyviocdOSxSZxF4aY FavOqmrxdWAfIE3OiwOdcNzdzfDL0pHhK6DZFTMCUnW+Q1BJgr0vt+PzS4oxzaR9CdKRnu I4FyJfGanB8km/tHOY0Ne4PR+/QBOWDff9PNdUnd/bepvoDpPvXMj8CpiM38GQW/EAkzwi hIV3oBswyaNUOVqEleGvQanKaZEgCA1yO+JmDOxbpTwQZRCpBeMssvIv12wu8A== From: Thomas Richard Date: Wed, 11 Dec 2024 17:27:18 +0100 Subject: [PATCH 3/5] gpiolib: add gpiochip_add_pinlist_range() function Precedence: bulk X-Mailing-List: linux-gpio@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20241211-aaeon-up-board-pinctrl-support-v1-3-24719be27631@bootlin.com> References: <20241211-aaeon-up-board-pinctrl-support-v1-0-24719be27631@bootlin.com> In-Reply-To: <20241211-aaeon-up-board-pinctrl-support-v1-0-24719be27631@bootlin.com> To: Linus Walleij , Bartosz Golaszewski , Lee Jones , Pavel Machek Cc: linux-gpio@vger.kernel.org, linux-kernel@vger.kernel.org, linux-leds@vger.kernel.org, thomas.petazzoni@bootlin.com, DanieleCleri@aaeon.eu, GaryWang@aaeon.com.tw, Thomas Richard X-Mailer: b4 0.14.1 X-GND-Sasl: thomas.richard@bootlin.com Add gpiochip_add_pinlist_range() function to add a range for GPIO <-> pin mapping, using a list of non consecutive pins. Previously, it was only possible to add range of consecutive pins using gpiochip_add_pin_range(). The struct pinctrl_gpio_range has a 'pins' member which allows to set a list of pins (which can be non consecutive). gpiochip_add_pinlist_range() is identical to gpiochip_add_pin_range(), except it set 'pins' member instead of 'pin_base' member. Signed-off-by: Thomas Richard --- drivers/gpio/gpiolib.c | 74 +++++++++++++++++++++++++++++++++------------ include/linux/gpio/driver.h | 12 ++++++++ 2 files changed, 66 insertions(+), 20 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 3c8ec7f3a83b..e0fe07167e62 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2227,26 +2227,9 @@ int gpiochip_add_pingroup_range(struct gpio_chip *gc, } EXPORT_SYMBOL_GPL(gpiochip_add_pingroup_range); -/** - * gpiochip_add_pin_range() - add a range for GPIO <-> pin mapping - * @gc: the gpiochip to add the range for - * @pinctl_name: the dev_name() of the pin controller to map to - * @gpio_offset: the start offset in the current gpio_chip number space - * @pin_offset: the start offset in the pin controller number space - * @npins: the number of pins from the offset of each pin space (GPIO and - * pin controller) to accumulate in this range - * - * Calling this function directly from a DeviceTree-supported - * pinctrl driver is DEPRECATED. Please see Section 2.1 of - * Documentation/devicetree/bindings/gpio/gpio.txt on how to - * bind pinctrl and gpio drivers via the "gpio-ranges" property. - * - * Returns: - * 0 on success, or a negative errno on failure. - */ -int gpiochip_add_pin_range(struct gpio_chip *gc, const char *pinctl_name, - unsigned int gpio_offset, unsigned int pin_offset, - unsigned int npins) +static int __gpiochip_add_pin_range(struct gpio_chip *gc, const char *pinctl_name, + unsigned int gpio_offset, unsigned int pin_offset, + unsigned int const *pins, unsigned int npins) { struct gpio_pin_range *pin_range; struct gpio_device *gdev = gc->gpiodev; @@ -2264,6 +2247,7 @@ int gpiochip_add_pin_range(struct gpio_chip *gc, const char *pinctl_name, pin_range->range.name = gc->label; pin_range->range.base = gdev->base + gpio_offset; pin_range->range.pin_base = pin_offset; + pin_range->range.pins = pins; pin_range->range.npins = npins; pin_range->pctldev = pinctrl_find_and_add_gpio_range(pinctl_name, &pin_range->range); @@ -2282,8 +2266,58 @@ int gpiochip_add_pin_range(struct gpio_chip *gc, const char *pinctl_name, return 0; } + +/** + * gpiochip_add_pin_range() - add a range for GPIO <-> pin mapping + * @gc: the gpiochip to add the range for + * @pinctl_name: the dev_name() of the pin controller to map to + * @gpio_offset: the start offset in the current gpio_chip number space + * @pin_offset: the start offset in the pin controller number space + * @npins: the number of pins from the offset of each pin space (GPIO and + * pin controller) to accumulate in this range + * + * Calling this function directly from a DeviceTree-supported + * pinctrl driver is DEPRECATED. Please see Section 2.1 of + * Documentation/devicetree/bindings/gpio/gpio.txt on how to + * bind pinctrl and gpio drivers via the "gpio-ranges" property. + * + * Returns: + * 0 on success, or a negative errno on failure. + */ +int gpiochip_add_pin_range(struct gpio_chip *gc, const char *pinctl_name, + unsigned int gpio_offset, unsigned int pin_offset, + unsigned int npins) +{ + return __gpiochip_add_pin_range(gc, pinctl_name, gpio_offset, + pin_offset, NULL, npins); +} EXPORT_SYMBOL_GPL(gpiochip_add_pin_range); +/** + * gpiochip_add_pinlist_range() - add a range for GPIO <-> pin mapping + * @gc: the gpiochip to add the range for + * @pinctl_name: the dev_name() of the pin controller to map to + * @gpio_offset: the start offset in the current gpio_chip number space + * @pin_list: the list of pins to accumulate in this range + * @npins: the number of pins to accumulate in this range + * + * Calling this function directly from a DeviceTree-supported + * pinctrl driver is DEPRECATED. Please see Section 2.1 of + * Documentation/devicetree/bindings/gpio/gpio.txt on how to + * bind pinctrl and gpio drivers via the "gpio-ranges" property. + * + * Returns: + * 0 on success, or a negative errno on failure. + */ +int gpiochip_add_pinlist_range(struct gpio_chip *gc, const char *pinctl_name, + unsigned int gpio_offset, unsigned int const *pins, + unsigned int npins) +{ + return __gpiochip_add_pin_range(gc, pinctl_name, gpio_offset, 0, pins, + npins); +} +EXPORT_SYMBOL_GPL(gpiochip_add_pinlist_range); + /** * gpiochip_remove_pin_ranges() - remove all the GPIO <-> pin mappings * @gc: the chip to remove all the mappings for diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index 2dd7cb9cc270..8037f9a1e2a9 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -751,6 +751,9 @@ struct gpio_pin_range { int gpiochip_add_pin_range(struct gpio_chip *gc, const char *pinctl_name, unsigned int gpio_offset, unsigned int pin_offset, unsigned int npins); +int gpiochip_add_pinlist_range(struct gpio_chip *gc, const char *pinctl_name, + unsigned int gpio_offset, unsigned int const *pins, + unsigned int npins); int gpiochip_add_pingroup_range(struct gpio_chip *gc, struct pinctrl_dev *pctldev, unsigned int gpio_offset, const char *pin_group); @@ -765,6 +768,15 @@ gpiochip_add_pin_range(struct gpio_chip *gc, const char *pinctl_name, { return 0; } + +static inline int +gpiochip_add_pinlist_range(struct gpio_chip *gc, const char *pinctl_name, + unsigned int gpio_offset, unsigned int const *pins, + unsigned int npins) +{ + return 0; +} + static inline int gpiochip_add_pingroup_range(struct gpio_chip *gc, struct pinctrl_dev *pctldev, From patchwork Wed Dec 11 16:27:19 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thomas Richard X-Patchwork-Id: 851125 Received: from relay1-d.mail.gandi.net (relay1-d.mail.gandi.net [217.70.183.193]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D1A751DD0EF; Wed, 11 Dec 2024 16:28:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.70.183.193 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1733934484; cv=none; b=NBNO2AuwF/Tb0xyBQzT+meF/tKOZKOJ1CrEmRfZM1Zt3MhhLNSRos9Ioo0NkggeXVPxu80xQi1odYUGUt4PHhx6yBY9UC3tWqw8WsCxYowUnvjY1bzJA8l7611Cdk6Kt9yXWYzi7Fj7j4eVj5xT0Mdik4DHKmeON5YjfJLUAYXQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1733934484; c=relaxed/simple; bh=inpb6atiuyfSmyjc+Iadd18wiiuS0y9+JoO5stDlNS0=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=frwVZe8nYh8iXAXBns/WSlliKqZM3tjwhpo/RS0biJRp5aLWehhXmD0+Az/WWv3yWGM6ygm+ILN62nh3wXza1iLDfq2xceT6kETnznmO9RFj5j3B+07EISp+8adnmpHTDucP9vhWyT6nA39iMS+5YyNREI8rBMfnREAI3pvJXBk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com; spf=pass smtp.mailfrom=bootlin.com; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b=dfvsdXV5; arc=none smtp.client-ip=217.70.183.193 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=bootlin.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b="dfvsdXV5" Received: by mail.gandi.net (Postfix) with ESMTPSA id 92C8624000E; Wed, 11 Dec 2024 16:27:52 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1733934473; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=bLxv4hK1oTztg9bcjuIJwihEmU+RVBrQocvVMbyAmlY=; b=dfvsdXV5/ehaCV0/ricWuovvWiLRdHsV/TRoYk0BxEcgrHzbllHsgo63yYDv6fpdzIO46I YYqSw/SZB7FGXCpymzWY1QhVh9HoOUc49DnAcpVr3Av8zbiJXqYeALQ+y8HdENGz9DAghu iOz01MaCTk7MtCUdqdsuUZQqdRFHK7rvrhsoJZ3DJXiGN8NswB5lNW1gzRfNNpekPuJXvq dWXkrFjssVMgkifCHXvLnL/tKuxSGLq1SMRJWBgBSaRwBjEMC3Ba2yYI/Ojl2U5zHvzuss E8pC3P2P6jmuZCc/8G2dnoKiOiI8J4FCrterlskxGoOK4EgCZqlk+G3fh2A/dg== From: Thomas Richard Date: Wed, 11 Dec 2024 17:27:19 +0100 Subject: [PATCH 4/5] pinctrl: Add pin controller driver for AAEON UP boards Precedence: bulk X-Mailing-List: linux-gpio@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20241211-aaeon-up-board-pinctrl-support-v1-4-24719be27631@bootlin.com> References: <20241211-aaeon-up-board-pinctrl-support-v1-0-24719be27631@bootlin.com> In-Reply-To: <20241211-aaeon-up-board-pinctrl-support-v1-0-24719be27631@bootlin.com> To: Linus Walleij , Bartosz Golaszewski , Lee Jones , Pavel Machek Cc: linux-gpio@vger.kernel.org, linux-kernel@vger.kernel.org, linux-leds@vger.kernel.org, thomas.petazzoni@bootlin.com, DanieleCleri@aaeon.eu, GaryWang@aaeon.com.tw, Thomas Richard X-Mailer: b4 0.14.1 X-GND-Sasl: thomas.richard@bootlin.com This enables the pin control support of the onboard FPGA on AAEON UP boards. Due to the hardware design, the driver shall control its pins in tandem with their corresponding Intel SoC GPIOs. UP boards and UP Squared boards are supported. Based on the work done by Gary Wang , largely rewritten. Signed-off-by: Thomas Richard --- drivers/pinctrl/Kconfig | 14 + drivers/pinctrl/Makefile | 1 + drivers/pinctrl/pinctrl-upboard.c | 1090 +++++++++++++++++++++++++++++++++++++ 3 files changed, 1105 insertions(+) diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 95a8e2b9a614..0289a0a6fb6d 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -576,6 +576,20 @@ config PINCTRL_TH1520 This driver is needed for RISC-V development boards like the BeagleV Ahead and the LicheePi 4A. +config PINCTRL_UPBOARD + tristate "AAeon UP board FPGA pin controller" + depends on MFD_UPBOARD_FPGA + select PINMUX + select GENERIC_PINCTRL_GROUPS + select GENERIC_PINMUX_FUNCTIONS + help + Pin controller for the FPGA GPIO lines on UP boards. Due to the + hardware layout, the driver control the FPGA pins 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 fba1c56624c0..989b8d28ecac 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -56,6 +56,7 @@ obj-$(CONFIG_PINCTRL_SX150X) += pinctrl-sx150x.o obj-$(CONFIG_PINCTRL_TB10X) += pinctrl-tb10x.o obj-$(CONFIG_PINCTRL_TPS6594) += pinctrl-tps6594.o obj-$(CONFIG_PINCTRL_TH1520) += pinctrl-th1520.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..14bf64b9587d --- /dev/null +++ b/drivers/pinctrl/pinctrl-upboard.c @@ -0,0 +1,1090 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * UP board pin control driver. + * + * FPGA provides more GPIO driving power, LEDS and pin mux function. + * + * Copyright (c) AAEON. All rights reserved. + * Copyright (C) 2024 Bootlin + * + * Author: Gary Wang + * Author: Thomas Richard + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core.h" +#include "pinmux.h" + +enum upboard_pin_mode { + UPBOARD_PIN_MODE_FUNCTION = 1, + UPBOARD_PIN_MODE_GPIO_IN, + UPBOARD_PIN_MODE_GPIO_OUT, + UPBOARD_PIN_MODE_DISABLED, +}; + +struct upboard_pin { + struct regmap_field *funcbit; + struct regmap_field *enbit; + struct regmap_field *dirbit; +}; + +struct upboard_pingroup { + struct pingroup grp; + enum upboard_pin_mode mode; + const enum upboard_pin_mode *modes; +}; + +struct upboard_pinctrl_data { + const struct upboard_pingroup *groups; + size_t ngroups; + const struct pinfunction *funcs; + size_t nfuncs; + const struct pinctrl_map *maps; + size_t nmaps; + const unsigned int *pin_header; + size_t ngpio; +}; + +struct upboard_pinctrl { + struct gpio_chip chip; + struct device *dev; + struct pinctrl_dev *pctldev; + const struct upboard_pinctrl_data *pctrl_data; + struct gpio_pin_range pin_range; + struct upboard_pin *pins; + struct gpio_desc **gpio; +}; + +enum upboard_func0_fpgabit { + UPBOARD_FUNC_I2C0_EN = 8, + UPBOARD_FUNC_I2C1_EN = 9, + UPBOARD_FUNC_CEC0_EN = 12, + UPBOARD_FUNC_ADC0_EN = 14, +}; + +static const struct reg_field upboard_i2c0_reg = + REG_FIELD(UPBOARD_REG_FUNC_EN0, UPBOARD_FUNC_I2C0_EN, UPBOARD_FUNC_I2C0_EN); + +static const struct reg_field upboard_i2c1_reg = + REG_FIELD(UPBOARD_REG_FUNC_EN0, UPBOARD_FUNC_I2C1_EN, UPBOARD_FUNC_I2C1_EN); + +static const struct reg_field upboard_adc0_reg = + REG_FIELD(UPBOARD_REG_FUNC_EN0, UPBOARD_FUNC_ADC0_EN, UPBOARD_FUNC_ADC0_EN); + +#define UPBOARD_UP_BIT_TO_PIN(bit) UPBOARD_UP_BIT_##bit + +#define UPBOARD_UP_PIN_NAME(id) \ + { \ + .number = UPBOARD_UP_BIT_##id, \ + .name = #id, \ + } + +#define UPBOARD_UP_PIN_MUX(bit, data) \ + { \ + .number = UPBOARD_UP_BIT_##bit, \ + .name = "PINMUX_"#bit, \ + .drv_data = (void *)(data), \ + } + +#define UPBOARD_UP_PIN_FUNC(id, data) \ + { \ + .number = UPBOARD_UP_BIT_##id, \ + .name = #id, \ + .drv_data = (void *)(data), \ + } + +enum upboard_up_fpgabit { + UPBOARD_UP_BIT_I2C1_SDA, + UPBOARD_UP_BIT_I2C1_SCL, + UPBOARD_UP_BIT_ADC0, + UPBOARD_UP_BIT_UART1_RTS, + UPBOARD_UP_BIT_GPIO27, + UPBOARD_UP_BIT_GPIO22, + UPBOARD_UP_BIT_SPI_MOSI, + UPBOARD_UP_BIT_SPI_MISO, + UPBOARD_UP_BIT_SPI_CLK, + UPBOARD_UP_BIT_I2C0_SDA, + UPBOARD_UP_BIT_GPIO5, + UPBOARD_UP_BIT_GPIO6, + UPBOARD_UP_BIT_PWM1, + UPBOARD_UP_BIT_I2S_FRM, + UPBOARD_UP_BIT_GPIO26, + UPBOARD_UP_BIT_UART1_TX, + UPBOARD_UP_BIT_UART1_RX, + UPBOARD_UP_BIT_I2S_CLK, + UPBOARD_UP_BIT_GPIO23, + UPBOARD_UP_BIT_GPIO24, + UPBOARD_UP_BIT_GPIO25, + UPBOARD_UP_BIT_SPI_CS0, + UPBOARD_UP_BIT_SPI_CS1, + UPBOARD_UP_BIT_I2C0_SCL, + UPBOARD_UP_BIT_PWM0, + UPBOARD_UP_BIT_UART1_CTS, + UPBOARD_UP_BIT_I2S_DIN, + UPBOARD_UP_BIT_I2S_DOUT, +}; + +static const struct pinctrl_pin_desc upboard_up_pins[] = { + UPBOARD_UP_PIN_FUNC(I2C1_SDA, &upboard_i2c1_reg), + UPBOARD_UP_PIN_FUNC(I2C1_SCL, &upboard_i2c1_reg), + UPBOARD_UP_PIN_FUNC(ADC0, &upboard_adc0_reg), + UPBOARD_UP_PIN_NAME(UART1_RTS), + UPBOARD_UP_PIN_NAME(GPIO27), + UPBOARD_UP_PIN_NAME(GPIO22), + UPBOARD_UP_PIN_NAME(SPI_MOSI), + UPBOARD_UP_PIN_NAME(SPI_MISO), + UPBOARD_UP_PIN_NAME(SPI_CLK), + UPBOARD_UP_PIN_FUNC(I2C0_SDA, &upboard_i2c0_reg), + UPBOARD_UP_PIN_NAME(GPIO5), + UPBOARD_UP_PIN_NAME(GPIO6), + UPBOARD_UP_PIN_NAME(PWM1), + UPBOARD_UP_PIN_NAME(I2S_FRM), + UPBOARD_UP_PIN_NAME(GPIO26), + UPBOARD_UP_PIN_NAME(UART1_TX), + UPBOARD_UP_PIN_NAME(UART1_RX), + UPBOARD_UP_PIN_NAME(I2S_CLK), + UPBOARD_UP_PIN_NAME(GPIO23), + UPBOARD_UP_PIN_NAME(GPIO24), + UPBOARD_UP_PIN_NAME(GPIO25), + UPBOARD_UP_PIN_NAME(SPI_CS0), + UPBOARD_UP_PIN_NAME(SPI_CS1), + UPBOARD_UP_PIN_FUNC(I2C0_SCL, &upboard_i2c0_reg), + UPBOARD_UP_PIN_NAME(PWM0), + UPBOARD_UP_PIN_NAME(UART1_CTS), + UPBOARD_UP_PIN_NAME(I2S_DIN), + UPBOARD_UP_PIN_NAME(I2S_DOUT), +}; + +static const unsigned int upboard_up_pin_header[] = { + UPBOARD_UP_BIT_TO_PIN(I2C0_SDA), + UPBOARD_UP_BIT_TO_PIN(I2C0_SCL), + UPBOARD_UP_BIT_TO_PIN(I2C1_SDA), + UPBOARD_UP_BIT_TO_PIN(I2C1_SCL), + UPBOARD_UP_BIT_TO_PIN(ADC0), + UPBOARD_UP_BIT_TO_PIN(GPIO5), + UPBOARD_UP_BIT_TO_PIN(GPIO6), + UPBOARD_UP_BIT_TO_PIN(SPI_CS1), + UPBOARD_UP_BIT_TO_PIN(SPI_CS0), + UPBOARD_UP_BIT_TO_PIN(SPI_MISO), + UPBOARD_UP_BIT_TO_PIN(SPI_MOSI), + UPBOARD_UP_BIT_TO_PIN(SPI_CLK), + UPBOARD_UP_BIT_TO_PIN(PWM0), + UPBOARD_UP_BIT_TO_PIN(PWM1), + UPBOARD_UP_BIT_TO_PIN(UART1_TX), + UPBOARD_UP_BIT_TO_PIN(UART1_RX), + UPBOARD_UP_BIT_TO_PIN(UART1_CTS), + UPBOARD_UP_BIT_TO_PIN(UART1_RTS), + UPBOARD_UP_BIT_TO_PIN(I2S_CLK), + UPBOARD_UP_BIT_TO_PIN(I2S_FRM), + UPBOARD_UP_BIT_TO_PIN(I2S_DIN), + UPBOARD_UP_BIT_TO_PIN(I2S_DOUT), + UPBOARD_UP_BIT_TO_PIN(GPIO22), + UPBOARD_UP_BIT_TO_PIN(GPIO23), + UPBOARD_UP_BIT_TO_PIN(GPIO24), + UPBOARD_UP_BIT_TO_PIN(GPIO25), + UPBOARD_UP_BIT_TO_PIN(GPIO26), + UPBOARD_UP_BIT_TO_PIN(GPIO27), +}; + +static const unsigned int upboard_up_uart1_pins[] = { + UPBOARD_UP_BIT_TO_PIN(UART1_TX), + UPBOARD_UP_BIT_TO_PIN(UART1_RX), + UPBOARD_UP_BIT_TO_PIN(UART1_RTS), + UPBOARD_UP_BIT_TO_PIN(UART1_CTS), +}; + +static const enum upboard_pin_mode upboard_up_uart1_modes[] = { + UPBOARD_PIN_MODE_GPIO_OUT, + UPBOARD_PIN_MODE_GPIO_IN, + UPBOARD_PIN_MODE_GPIO_OUT, + UPBOARD_PIN_MODE_GPIO_IN, +}; + +static_assert(ARRAY_SIZE(upboard_up_uart1_modes) == + ARRAY_SIZE(upboard_up_uart1_pins)); + +static const unsigned int upboard_up_i2c0_pins[] = { + UPBOARD_UP_BIT_TO_PIN(I2C0_SCL), + UPBOARD_UP_BIT_TO_PIN(I2C0_SDA), +}; + +static const unsigned int upboard_up_i2c1_pins[] = { + UPBOARD_UP_BIT_TO_PIN(I2C1_SCL), + UPBOARD_UP_BIT_TO_PIN(I2C1_SDA), +}; + +static const unsigned int upboard_up_spi2_pins[] = { + UPBOARD_UP_BIT_TO_PIN(SPI_MOSI), + UPBOARD_UP_BIT_TO_PIN(SPI_MISO), + UPBOARD_UP_BIT_TO_PIN(SPI_CLK), + UPBOARD_UP_BIT_TO_PIN(SPI_CS0), + UPBOARD_UP_BIT_TO_PIN(SPI_CS1), +}; + +static const enum upboard_pin_mode upboard_up_spi2_modes[] = { + UPBOARD_PIN_MODE_GPIO_OUT, + UPBOARD_PIN_MODE_GPIO_IN, + UPBOARD_PIN_MODE_GPIO_OUT, + UPBOARD_PIN_MODE_GPIO_OUT, + UPBOARD_PIN_MODE_GPIO_OUT, +}; + +static_assert(ARRAY_SIZE(upboard_up_spi2_modes) == + ARRAY_SIZE(upboard_up_spi2_pins)); + +static const unsigned int upboard_up_i2s0_pins[] = { + UPBOARD_UP_BIT_TO_PIN(I2S_FRM), + UPBOARD_UP_BIT_TO_PIN(I2S_CLK), + UPBOARD_UP_BIT_TO_PIN(I2S_DIN), + UPBOARD_UP_BIT_TO_PIN(I2S_DOUT), +}; + +static const enum upboard_pin_mode upboard_up_i2s0_modes[] = { + UPBOARD_PIN_MODE_GPIO_OUT, + UPBOARD_PIN_MODE_GPIO_OUT, + UPBOARD_PIN_MODE_GPIO_IN, + UPBOARD_PIN_MODE_GPIO_OUT, +}; + +static_assert(ARRAY_SIZE(upboard_up_i2s0_pins) == + ARRAY_SIZE(upboard_up_i2s0_modes)); + +static const unsigned int upboard_up_pwm0_pins[] = { + UPBOARD_UP_BIT_TO_PIN(PWM0), +}; + +static const unsigned int upboard_up_pwm1_pins[] = { + UPBOARD_UP_BIT_TO_PIN(PWM1), +}; + +static const unsigned int upboard_up_adc0_pins[] = { + UPBOARD_UP_BIT_TO_PIN(ADC0), +}; + +#define UPBOARD_PINGROUP_MODE(_grp, _pins, _mode) \ +{ \ + .grp = PINCTRL_PINGROUP(_grp, _pins, ARRAY_SIZE(_pins)), \ + .mode = _mode, \ +} + +#define UPBOARD_PINGROUP_MODES(_grp, _pins, _modes) \ +{ \ + .grp = PINCTRL_PINGROUP(_grp, _pins, ARRAY_SIZE(_pins)), \ + .modes = _modes, \ +} + +static const struct upboard_pingroup upboard_up_pin_groups[] = { + UPBOARD_PINGROUP_MODES("uart1_grp", upboard_up_uart1_pins, upboard_up_uart1_modes), + UPBOARD_PINGROUP_MODE("i2c0_grp", upboard_up_i2c0_pins, UPBOARD_PIN_MODE_GPIO_OUT), + UPBOARD_PINGROUP_MODE("i2c1_grp", upboard_up_i2c1_pins, UPBOARD_PIN_MODE_GPIO_OUT), + UPBOARD_PINGROUP_MODES("spi2_grp", upboard_up_spi2_pins, &upboard_up_spi2_modes[0]), + UPBOARD_PINGROUP_MODES("i2s0_grp", upboard_up_i2s0_pins, &upboard_up_i2s0_modes[0]), + UPBOARD_PINGROUP_MODE("pwm0_grp", upboard_up_pwm0_pins, UPBOARD_PIN_MODE_GPIO_OUT), + UPBOARD_PINGROUP_MODE("pwm1_grp", upboard_up_pwm1_pins, UPBOARD_PIN_MODE_GPIO_OUT), + UPBOARD_PINGROUP_MODE("adc0_grp", upboard_up_adc0_pins, UPBOARD_PIN_MODE_GPIO_IN), +}; + +static const char * const upboard_up_uart1_groups[] = { "uart1_grp" }; +static const char * const upboard_up_i2c0_groups[] = { "i2c0_grp" }; +static const char * const upboard_up_i2c1_groups[] = { "i2c1_grp" }; +static const char * const upboard_up_spi2_groups[] = { "spi2_grp" }; +static const char * const upboard_up_i2s0_groups[] = { "i2s0_grp" }; +static const char * const upboard_up_pwm0_groups[] = { "pwm0_grp" }; +static const char * const upboard_up_pwm1_groups[] = { "pwm1_grp" }; +static const char * const upboard_up_adc0_groups[] = { "adc0_grp" }; + +#define UPBOARD_FUNCTION(func, groups) PINCTRL_PINFUNCTION(func, groups, ARRAY_SIZE(groups)) + +static const struct pinfunction upboard_up_pin_functions[] = { + UPBOARD_FUNCTION("uart1", upboard_up_uart1_groups), + UPBOARD_FUNCTION("i2c0", upboard_up_i2c0_groups), + UPBOARD_FUNCTION("i2c1", upboard_up_i2c1_groups), + UPBOARD_FUNCTION("spi2", upboard_up_spi2_groups), + UPBOARD_FUNCTION("i2s0", upboard_up_i2s0_groups), + UPBOARD_FUNCTION("pwm0", upboard_up_pwm0_groups), + UPBOARD_FUNCTION("pwm1", upboard_up_pwm1_groups), + UPBOARD_FUNCTION("adc0", upboard_up_adc0_groups), +}; + +static const struct pinctrl_map upboard_up_pin_mapping[] = { + PIN_MAP_MUX_GROUP_HOG_DEFAULT("upboard-pinctrl", "uart1_grp", "uart1"), + PIN_MAP_MUX_GROUP_HOG_DEFAULT("upboard-pinctrl", "i2c0_grp", "i2c0"), + PIN_MAP_MUX_GROUP_HOG_DEFAULT("upboard-pinctrl", "i2c1_grp", "i2c1"), + PIN_MAP_MUX_GROUP_HOG_DEFAULT("upboard-pinctrl", "spi2_grp", "spi2"), + PIN_MAP_MUX_GROUP_HOG_DEFAULT("upboard-pinctrl", "i2s0_grp", "i2s0"), + PIN_MAP_MUX_GROUP_HOG_DEFAULT("upboard-pinctrl", "pwm0_grp", "pwm0"), + PIN_MAP_MUX_GROUP_HOG_DEFAULT("upboard-pinctrl", "pwm1_grp", "pwm1"), + PIN_MAP_MUX_GROUP_HOG_DEFAULT("upboard-pinctrl", "adc0_grp", "adc0"), +}; + +static const struct upboard_pinctrl_data upboard_up_pinctrl_data = { + .groups = &upboard_up_pin_groups[0], + .ngroups = ARRAY_SIZE(upboard_up_pin_groups), + .funcs = &upboard_up_pin_functions[0], + .nfuncs = ARRAY_SIZE(upboard_up_pin_functions), + .maps = &upboard_up_pin_mapping[0], + .nmaps = ARRAY_SIZE(upboard_up_pin_mapping), + .pin_header = &upboard_up_pin_header[0], + .ngpio = ARRAY_SIZE(upboard_up_pin_header), +}; + +#define UPBOARD_UP2_BIT_TO_PIN(bit) UPBOARD_UP2_BIT_##bit + +#define UPBOARD_UP2_PIN_NAME(id) \ + { \ + .number = UPBOARD_UP2_BIT_##id, \ + .name = #id, \ + } + +#define UPBOARD_UP2_PIN_MUX(bit, data) \ + { \ + .number = UPBOARD_UP2_BIT_##bit, \ + .name = "PINMUX_"#bit, \ + .drv_data = (void *)(data), \ + } + +#define UPBOARD_UP2_PIN_FUNC(id, data) \ + { \ + .number = UPBOARD_UP2_BIT_##id, \ + .name = #id, \ + .drv_data = (void *)(data), \ + } + +enum upboard_up2_fpgabit { + UPBOARD_UP2_BIT_UART1_TXD, + UPBOARD_UP2_BIT_UART1_RXD, + UPBOARD_UP2_BIT_UART1_RTS, + UPBOARD_UP2_BIT_UART1_CTS, + UPBOARD_UP2_BIT_GPIO3_ADC0, + UPBOARD_UP2_BIT_GPIO5_ADC2, + UPBOARD_UP2_BIT_GPIO6_ADC3, + UPBOARD_UP2_BIT_GPIO11, + UPBOARD_UP2_BIT_EXHAT_LVDS1n, + UPBOARD_UP2_BIT_EXHAT_LVDS1p, + UPBOARD_UP2_BIT_SPI2_TXD, + UPBOARD_UP2_BIT_SPI2_RXD, + UPBOARD_UP2_BIT_SPI2_FS1, + UPBOARD_UP2_BIT_SPI2_FS0, + UPBOARD_UP2_BIT_SPI2_CLK, + UPBOARD_UP2_BIT_SPI1_TXD, + UPBOARD_UP2_BIT_SPI1_RXD, + UPBOARD_UP2_BIT_SPI1_FS1, + UPBOARD_UP2_BIT_SPI1_FS0, + UPBOARD_UP2_BIT_SPI1_CLK, + UPBOARD_UP2_BIT_I2C0_SCL, + UPBOARD_UP2_BIT_I2C0_SDA, + UPBOARD_UP2_BIT_I2C1_SCL, + UPBOARD_UP2_BIT_I2C1_SDA, + UPBOARD_UP2_BIT_PWM1, + UPBOARD_UP2_BIT_PWM0, + UPBOARD_UP2_BIT_EXHAT_LVDS0n, + UPBOARD_UP2_BIT_EXHAT_LVDS0p, + UPBOARD_UP2_BIT_GPIO24, + UPBOARD_UP2_BIT_GPIO10, + UPBOARD_UP2_BIT_GPIO2, + UPBOARD_UP2_BIT_GPIO1, + UPBOARD_UP2_BIT_EXHAT_LVDS3n, + UPBOARD_UP2_BIT_EXHAT_LVDS3p, + UPBOARD_UP2_BIT_EXHAT_LVDS4n, + UPBOARD_UP2_BIT_EXHAT_LVDS4p, + UPBOARD_UP2_BIT_EXHAT_LVDS5n, + UPBOARD_UP2_BIT_EXHAT_LVDS5p, + UPBOARD_UP2_BIT_I2S_SDO, + UPBOARD_UP2_BIT_I2S_SDI, + UPBOARD_UP2_BIT_I2S_WS_SYNC, + UPBOARD_UP2_BIT_I2S_BCLK, + UPBOARD_UP2_BIT_EXHAT_LVDS6n, + UPBOARD_UP2_BIT_EXHAT_LVDS6p, + UPBOARD_UP2_BIT_EXHAT_LVDS7n, + UPBOARD_UP2_BIT_EXHAT_LVDS7p, + UPBOARD_UP2_BIT_EXHAT_LVDS2n, + UPBOARD_UP2_BIT_EXHAT_LVDS2p, +}; + +static const struct pinctrl_pin_desc upboard_up2_pins[] = { + UPBOARD_UP2_PIN_NAME(UART1_TXD), + UPBOARD_UP2_PIN_NAME(UART1_RXD), + UPBOARD_UP2_PIN_NAME(UART1_RTS), + UPBOARD_UP2_PIN_NAME(UART1_CTS), + UPBOARD_UP2_PIN_NAME(GPIO3_ADC0), + UPBOARD_UP2_PIN_NAME(GPIO5_ADC2), + UPBOARD_UP2_PIN_NAME(GPIO6_ADC3), + UPBOARD_UP2_PIN_NAME(GPIO11), + UPBOARD_UP2_PIN_NAME(EXHAT_LVDS1n), + UPBOARD_UP2_PIN_NAME(EXHAT_LVDS1p), + UPBOARD_UP2_PIN_NAME(SPI2_TXD), + UPBOARD_UP2_PIN_NAME(SPI2_RXD), + UPBOARD_UP2_PIN_NAME(SPI2_FS1), + UPBOARD_UP2_PIN_NAME(SPI2_FS0), + UPBOARD_UP2_PIN_NAME(SPI2_CLK), + UPBOARD_UP2_PIN_NAME(SPI1_TXD), + UPBOARD_UP2_PIN_NAME(SPI1_RXD), + UPBOARD_UP2_PIN_NAME(SPI1_FS1), + UPBOARD_UP2_PIN_NAME(SPI1_FS0), + UPBOARD_UP2_PIN_NAME(SPI1_CLK), + UPBOARD_UP2_PIN_MUX(I2C0_SCL, &upboard_i2c0_reg), + UPBOARD_UP2_PIN_MUX(I2C0_SDA, &upboard_i2c0_reg), + UPBOARD_UP2_PIN_MUX(I2C1_SCL, &upboard_i2c1_reg), + UPBOARD_UP2_PIN_MUX(I2C1_SDA, &upboard_i2c1_reg), + UPBOARD_UP2_PIN_NAME(PWM1), + UPBOARD_UP2_PIN_NAME(PWM0), + UPBOARD_UP2_PIN_NAME(EXHAT_LVDS0n), + UPBOARD_UP2_PIN_NAME(EXHAT_LVDS0p), + UPBOARD_UP2_PIN_MUX(GPIO24, &upboard_i2c0_reg), + UPBOARD_UP2_PIN_MUX(GPIO10, &upboard_i2c0_reg), + UPBOARD_UP2_PIN_MUX(GPIO2, &upboard_i2c1_reg), + UPBOARD_UP2_PIN_MUX(GPIO1, &upboard_i2c1_reg), + UPBOARD_UP2_PIN_NAME(EXHAT_LVDS3n), + UPBOARD_UP2_PIN_NAME(EXHAT_LVDS3p), + UPBOARD_UP2_PIN_NAME(EXHAT_LVDS4n), + UPBOARD_UP2_PIN_NAME(EXHAT_LVDS4p), + UPBOARD_UP2_PIN_NAME(EXHAT_LVDS5n), + UPBOARD_UP2_PIN_NAME(EXHAT_LVDS5p), + UPBOARD_UP2_PIN_NAME(I2S_SDO), + UPBOARD_UP2_PIN_NAME(I2S_SDI), + UPBOARD_UP2_PIN_NAME(I2S_WS_SYNC), + UPBOARD_UP2_PIN_NAME(I2S_BCLK), + UPBOARD_UP2_PIN_NAME(EXHAT_LVDS6n), + UPBOARD_UP2_PIN_NAME(EXHAT_LVDS6p), + UPBOARD_UP2_PIN_NAME(EXHAT_LVDS7n), + UPBOARD_UP2_PIN_NAME(EXHAT_LVDS7p), + UPBOARD_UP2_PIN_NAME(EXHAT_LVDS2n), + UPBOARD_UP2_PIN_NAME(EXHAT_LVDS2p), +}; + +static const unsigned int upboard_up2_pin_header[] = { + UPBOARD_UP2_BIT_TO_PIN(GPIO10), + UPBOARD_UP2_BIT_TO_PIN(GPIO24), + UPBOARD_UP2_BIT_TO_PIN(GPIO1), + UPBOARD_UP2_BIT_TO_PIN(GPIO2), + UPBOARD_UP2_BIT_TO_PIN(GPIO3_ADC0), + UPBOARD_UP2_BIT_TO_PIN(GPIO11), + UPBOARD_UP2_BIT_TO_PIN(SPI2_CLK), + UPBOARD_UP2_BIT_TO_PIN(SPI1_FS1), + UPBOARD_UP2_BIT_TO_PIN(SPI1_FS0), + UPBOARD_UP2_BIT_TO_PIN(SPI1_RXD), + UPBOARD_UP2_BIT_TO_PIN(SPI1_TXD), + UPBOARD_UP2_BIT_TO_PIN(SPI1_CLK), + UPBOARD_UP2_BIT_TO_PIN(PWM0), + UPBOARD_UP2_BIT_TO_PIN(PWM1), + UPBOARD_UP2_BIT_TO_PIN(UART1_TXD), + UPBOARD_UP2_BIT_TO_PIN(UART1_RXD), + UPBOARD_UP2_BIT_TO_PIN(UART1_CTS), + UPBOARD_UP2_BIT_TO_PIN(UART1_RTS), + UPBOARD_UP2_BIT_TO_PIN(I2S_BCLK), + UPBOARD_UP2_BIT_TO_PIN(I2S_WS_SYNC), + UPBOARD_UP2_BIT_TO_PIN(I2S_SDI), + UPBOARD_UP2_BIT_TO_PIN(I2S_SDO), + UPBOARD_UP2_BIT_TO_PIN(GPIO6_ADC3), + UPBOARD_UP2_BIT_TO_PIN(SPI2_FS1), + UPBOARD_UP2_BIT_TO_PIN(SPI2_RXD), + UPBOARD_UP2_BIT_TO_PIN(SPI2_TXD), + UPBOARD_UP2_BIT_TO_PIN(SPI2_FS0), + UPBOARD_UP2_BIT_TO_PIN(GPIO5_ADC2), +}; + +static const unsigned int upboard_up2_uart1_pins[] = { + UPBOARD_UP2_BIT_TO_PIN(UART1_TXD), + UPBOARD_UP2_BIT_TO_PIN(UART1_RXD), + UPBOARD_UP2_BIT_TO_PIN(UART1_RTS), + UPBOARD_UP2_BIT_TO_PIN(UART1_CTS), +}; + +static const enum upboard_pin_mode upboard_up2_uart1_modes[] = { + UPBOARD_PIN_MODE_GPIO_OUT, + UPBOARD_PIN_MODE_GPIO_IN, + UPBOARD_PIN_MODE_GPIO_OUT, + UPBOARD_PIN_MODE_GPIO_IN +}; + +static_assert(ARRAY_SIZE(upboard_up2_uart1_modes) == + ARRAY_SIZE(upboard_up2_uart1_pins)); + +static const unsigned int upboard_up2_i2c0_pins[] = { + UPBOARD_UP2_BIT_TO_PIN(I2C0_SCL), + UPBOARD_UP2_BIT_TO_PIN(I2C0_SDA), + UPBOARD_UP2_BIT_TO_PIN(GPIO24), + UPBOARD_UP2_BIT_TO_PIN(GPIO10), +}; + +static const unsigned int upboard_up2_i2c1_pins[] = { + UPBOARD_UP2_BIT_TO_PIN(I2C1_SCL), + UPBOARD_UP2_BIT_TO_PIN(I2C1_SDA), + UPBOARD_UP2_BIT_TO_PIN(GPIO2), + UPBOARD_UP2_BIT_TO_PIN(GPIO1), +}; + +static const unsigned int upboard_up2_spi1_pins[] = { + UPBOARD_UP2_BIT_TO_PIN(SPI1_TXD), + UPBOARD_UP2_BIT_TO_PIN(SPI1_RXD), + UPBOARD_UP2_BIT_TO_PIN(SPI1_FS1), + UPBOARD_UP2_BIT_TO_PIN(SPI1_FS0), + UPBOARD_UP2_BIT_TO_PIN(SPI1_CLK), +}; + +static const unsigned int upboard_up2_spi2_pins[] = { + UPBOARD_UP2_BIT_TO_PIN(SPI2_TXD), + UPBOARD_UP2_BIT_TO_PIN(SPI2_RXD), + UPBOARD_UP2_BIT_TO_PIN(SPI2_FS1), + UPBOARD_UP2_BIT_TO_PIN(SPI2_FS0), + UPBOARD_UP2_BIT_TO_PIN(SPI2_CLK), +}; + +static const enum upboard_pin_mode upboard_up2_spi_modes[] = { + UPBOARD_PIN_MODE_GPIO_OUT, + UPBOARD_PIN_MODE_GPIO_IN, + UPBOARD_PIN_MODE_GPIO_OUT, + UPBOARD_PIN_MODE_GPIO_OUT, + UPBOARD_PIN_MODE_GPIO_OUT, +}; + +static_assert(ARRAY_SIZE(upboard_up2_spi_modes) == + ARRAY_SIZE(upboard_up2_spi1_pins)); + +static_assert(ARRAY_SIZE(upboard_up2_spi_modes) == + ARRAY_SIZE(upboard_up2_spi2_pins)); + +static const unsigned int upboard_up2_i2s0_pins[] = { + UPBOARD_UP2_BIT_TO_PIN(I2S_BCLK), + UPBOARD_UP2_BIT_TO_PIN(I2S_WS_SYNC), + UPBOARD_UP2_BIT_TO_PIN(I2S_SDI), + UPBOARD_UP2_BIT_TO_PIN(I2S_SDO), +}; + +static const enum upboard_pin_mode upboard_up2_i2s0_modes[] = { + UPBOARD_PIN_MODE_GPIO_OUT, + UPBOARD_PIN_MODE_GPIO_OUT, + UPBOARD_PIN_MODE_GPIO_IN, + UPBOARD_PIN_MODE_GPIO_OUT +}; + +static_assert(ARRAY_SIZE(upboard_up2_i2s0_modes) == + ARRAY_SIZE(upboard_up2_i2s0_pins)); + +static const unsigned int upboard_up2_pwm0_pins[] = { + UPBOARD_UP2_BIT_TO_PIN(PWM0), +}; + +static const unsigned int upboard_up2_pwm1_pins[] = { + UPBOARD_UP2_BIT_TO_PIN(PWM1), +}; + +static const unsigned int upboard_up2_adc0_pins[] = { + UPBOARD_UP2_BIT_TO_PIN(GPIO3_ADC0), +}; + +static const unsigned int upboard_up2_adc2_pins[] = { + UPBOARD_UP2_BIT_TO_PIN(GPIO5_ADC2), +}; + +static const unsigned int upboard_up2_adc3_pins[] = { + UPBOARD_UP2_BIT_TO_PIN(GPIO6_ADC3), +}; + +static const struct upboard_pingroup upboard_up2_pin_groups[] = { + UPBOARD_PINGROUP_MODES("uart1_grp", upboard_up2_uart1_pins, upboard_up2_uart1_modes), + UPBOARD_PINGROUP_MODE("i2c0_grp", upboard_up2_i2c0_pins, UPBOARD_PIN_MODE_FUNCTION), + UPBOARD_PINGROUP_MODE("i2c1_grp", upboard_up2_i2c1_pins, UPBOARD_PIN_MODE_FUNCTION), + UPBOARD_PINGROUP_MODES("spi1_grp", upboard_up2_spi1_pins, upboard_up2_spi_modes), + UPBOARD_PINGROUP_MODES("spi2_grp", upboard_up2_spi2_pins, upboard_up2_spi_modes), + UPBOARD_PINGROUP_MODES("i2s0_grp", upboard_up2_i2s0_pins, upboard_up2_i2s0_modes), + UPBOARD_PINGROUP_MODE("pwm0_grp", upboard_up2_pwm0_pins, UPBOARD_PIN_MODE_GPIO_OUT), + UPBOARD_PINGROUP_MODE("pwm1_grp", upboard_up2_pwm1_pins, UPBOARD_PIN_MODE_GPIO_OUT), + UPBOARD_PINGROUP_MODE("adc0_grp", upboard_up2_adc0_pins, UPBOARD_PIN_MODE_GPIO_IN), + UPBOARD_PINGROUP_MODE("adc2_grp", upboard_up2_adc2_pins, UPBOARD_PIN_MODE_GPIO_IN), + UPBOARD_PINGROUP_MODE("adc3_grp", upboard_up2_adc3_pins, UPBOARD_PIN_MODE_GPIO_IN), +}; + +static const char * const upboard_up2_uart1_groups[] = { "uart1_grp" }; +static const char * const upboard_up2_i2c0_groups[] = { "i2c0_grp" }; +static const char * const upboard_up2_i2c1_groups[] = { "i2c1_grp" }; +static const char * const upboard_up2_spi1_groups[] = { "spi1_grp" }; +static const char * const upboard_up2_spi2_groups[] = { "spi2_grp" }; +static const char * const upboard_up2_i2s0_groups[] = { "i2s0_grp" }; +static const char * const upboard_up2_pwm0_groups[] = { "pwm0_grp" }; +static const char * const upboard_up2_pwm1_groups[] = { "pwm1_grp" }; +static const char * const upboard_up2_adc0_groups[] = { "adc0_grp" }; +static const char * const upboard_up2_adc2_groups[] = { "adc2_grp" }; +static const char * const upboard_up2_adc3_groups[] = { "adc3_grp" }; + +static const struct pinfunction upboard_up2_pin_functions[] = { + UPBOARD_FUNCTION("uart1", upboard_up2_uart1_groups), + UPBOARD_FUNCTION("i2c0", upboard_up2_i2c0_groups), + UPBOARD_FUNCTION("i2c1", upboard_up2_i2c1_groups), + UPBOARD_FUNCTION("spi1", upboard_up2_spi1_groups), + UPBOARD_FUNCTION("spi2", upboard_up2_spi2_groups), + UPBOARD_FUNCTION("i2s0", upboard_up2_i2s0_groups), + UPBOARD_FUNCTION("pwm0", upboard_up2_pwm0_groups), + UPBOARD_FUNCTION("pwm1", upboard_up2_pwm1_groups), + UPBOARD_FUNCTION("adc0", upboard_up2_adc0_groups), + UPBOARD_FUNCTION("adc2", upboard_up2_adc2_groups), + UPBOARD_FUNCTION("adc3", upboard_up2_adc3_groups), +}; + +static const struct pinctrl_map upboard_up2_pin_mapping[] = { + PIN_MAP_MUX_GROUP_HOG_DEFAULT("upboard-pinctrl", "uart1_grp", "uart1"), + PIN_MAP_MUX_GROUP_HOG_DEFAULT("upboard-pinctrl", "i2c0_grp", "i2c0"), + PIN_MAP_MUX_GROUP_HOG_DEFAULT("upboard-pinctrl", "i2c1_grp", "i2c1"), + PIN_MAP_MUX_GROUP_HOG_DEFAULT("upboard-pinctrl", "spi1_grp", "spi1"), + PIN_MAP_MUX_GROUP_HOG_DEFAULT("upboard-pinctrl", "spi2_grp", "spi2"), + PIN_MAP_MUX_GROUP_HOG_DEFAULT("upboard-pinctrl", "i2s0_grp", "i2s0"), + PIN_MAP_MUX_GROUP_HOG_DEFAULT("upboard-pinctrl", "pwm0_grp", "pwm0"), + PIN_MAP_MUX_GROUP_HOG_DEFAULT("upboard-pinctrl", "pwm1_grp", "pwm1"), + PIN_MAP_MUX_GROUP_HOG_DEFAULT("upboard-pinctrl", "adc0_grp", "adc0"), + PIN_MAP_MUX_GROUP_HOG_DEFAULT("upboard-pinctrl", "adc2_grp", "adc2"), + PIN_MAP_MUX_GROUP_HOG_DEFAULT("upboard-pinctrl", "adc3_grp", "adc3"), +}; + +static const struct upboard_pinctrl_data upboard_up2_pinctrl_data = { + .groups = &upboard_up2_pin_groups[0], + .ngroups = ARRAY_SIZE(upboard_up2_pin_groups), + .funcs = &upboard_up2_pin_functions[0], + .nfuncs = ARRAY_SIZE(upboard_up2_pin_functions), + .maps = &upboard_up2_pin_mapping[0], + .nmaps = ARRAY_SIZE(upboard_up2_pin_mapping), + .pin_header = &upboard_up2_pin_header[0], + .ngpio = ARRAY_SIZE(upboard_up2_pin_header), +}; + +static int __upboard_pinctrl_gpio_request_enable(struct pinctrl_dev *pctldev, + unsigned int offset) +{ + struct upboard_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + struct upboard_pin *p = &pctrl->pins[offset]; + + if (p->funcbit) { + int ret = regmap_field_write(p->funcbit, 0); + + if (ret) + return ret; + } + + return regmap_field_write(p->enbit, 1); +} + +static int upboard_pinctrl_gpio_request_enable(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned int offset) +{ + return __upboard_pinctrl_gpio_request_enable(pctldev, offset); +} + +static void __upboard_pinctrl_gpio_disable_free(struct pinctrl_dev *pctldev, unsigned int offset) +{ + struct upboard_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + struct upboard_pin *p = &pctrl->pins[offset]; + + regmap_field_write(p->enbit, 0); + + if (p->funcbit) + regmap_field_write(p->funcbit, 1); +}; + +static void upboard_pinctrl_gpio_disable_free(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, unsigned int offset) +{ + return __upboard_pinctrl_gpio_disable_free(pctldev, offset); +} + +static int __upboard_pinctrl_gpio_set_direction(struct pinctrl_dev *pctldev, + unsigned int offset, bool input) +{ + struct upboard_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + struct upboard_pin *p = &pctrl->pins[offset]; + + return regmap_field_write(p->dirbit, input); +} + +static int upboard_pinctrl_gpio_set_direction(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned int offset, bool input) +{ + return __upboard_pinctrl_gpio_set_direction(pctldev, offset, input); +} + +static int upboard_pinctrl_set_mux(struct pinctrl_dev *pctldev, unsigned int func_selector, + unsigned int group_selector) +{ + struct upboard_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + const struct upboard_pinctrl_data *pctrl_data = pctrl->pctrl_data; + const struct upboard_pingroup *upgroups = pctrl_data->groups; + struct group_desc *grp; + int mode, ret, i; + + grp = pinctrl_generic_get_group(pctldev, group_selector); + if (!grp) + return -EINVAL; + + for (i = 0; i < grp->grp.npins; i++) { + mode = upgroups[group_selector].mode ? upgroups[group_selector].mode : + upgroups[group_selector].modes[i]; + + if (mode == UPBOARD_PIN_MODE_FUNCTION) { + __upboard_pinctrl_gpio_disable_free(pctldev, grp->grp.pins[i]); + continue; + } + + ret = __upboard_pinctrl_gpio_request_enable(pctldev, grp->grp.pins[i]); + if (ret) + return ret; + + ret = __upboard_pinctrl_gpio_set_direction(pctldev, grp->grp.pins[i], + mode == UPBOARD_PIN_MODE_GPIO_IN ? + true : false); + if (ret) + return ret; + } + + return 0; +} + +static const struct pinmux_ops upboard_pinmux_ops = { + .get_functions_count = pinmux_generic_get_function_count, + .get_function_name = pinmux_generic_get_function_name, + .get_function_groups = pinmux_generic_get_function_groups, + .set_mux = upboard_pinctrl_set_mux, + .gpio_request_enable = upboard_pinctrl_gpio_request_enable, + .gpio_disable_free = upboard_pinctrl_gpio_disable_free, + .gpio_set_direction = upboard_pinctrl_gpio_set_direction, +}; + +static int upboard_pinctrl_pin_get_mode(struct pinctrl_dev *pctldev, unsigned int pin) +{ + struct upboard_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + struct upboard_pin *p = &pctrl->pins[pin]; + + unsigned int val; + int ret; + + if (p->funcbit) { + ret = regmap_field_read(p->funcbit, &val); + if (ret) + return ret; + if (val) + return UPBOARD_PIN_MODE_FUNCTION; + } + + ret = regmap_field_read(p->enbit, &val); + if (ret) + return ret; + if (!val) + return UPBOARD_PIN_MODE_DISABLED; + + ret = regmap_field_read(p->dirbit, &val); + if (ret) + return ret; + + return val ? UPBOARD_PIN_MODE_GPIO_IN : UPBOARD_PIN_MODE_GPIO_OUT; +} + +static void upboard_pinctrl_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s, + unsigned int offset) +{ + int ret = upboard_pinctrl_pin_get_mode(pctldev, offset); + + if (ret == UPBOARD_PIN_MODE_FUNCTION) + seq_puts(s, "mode function "); + else if (ret == UPBOARD_PIN_MODE_DISABLED) + seq_puts(s, "HIGH-Z"); + else + seq_printf(s, "GPIO (%s) ", ret == UPBOARD_PIN_MODE_GPIO_IN ? "input" : "output"); +} + +static const struct pinctrl_ops upboard_pinctrl_ops = { + .get_groups_count = pinctrl_generic_get_group_count, + .get_group_name = pinctrl_generic_get_group_name, + .get_group_pins = pinctrl_generic_get_group_pins, + .pin_dbg_show = upboard_pinctrl_dbg_show, +}; + +static int upboard_gpio_request(struct gpio_chip *gc, unsigned int offset) +{ + struct upboard_pinctrl *pctrl = container_of(gc, struct upboard_pinctrl, chip); + unsigned int pin = pctrl->pctrl_data->pin_header[offset]; + int ret; + + ret = pinctrl_gpio_request(gc, offset); + if (ret) + return ret; + + pctrl->gpio[offset] = gpiod_get_index(pctrl->dev, "external", pin, 0); + + if (IS_ERR(pctrl->gpio[offset])) { + pinctrl_gpio_free(gc, offset); + return PTR_ERR(pctrl->gpio[offset]); + } + + return 0; +} + +static void upboard_gpio_free(struct gpio_chip *gc, unsigned int offset) +{ + struct upboard_pinctrl *pctrl = container_of(gc, struct upboard_pinctrl, chip); + + gpiod_put(pctrl->gpio[offset]); + + pinctrl_gpio_free(gc, offset); +} + +static int upboard_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) +{ + struct upboard_pinctrl *pctrl = container_of(gc, struct upboard_pinctrl, chip); + unsigned int pin = pctrl->pctrl_data->pin_header[offset]; + int mode; + + if (pctrl->gpio[offset]) + return gpiod_get_direction(pctrl->gpio[offset]); + + /* + * GPIO was not requested so SoC pin is probably not in GPIO mode. + * When a gpio_chip is registered, the core calls get_direction() for all lines. + * At this time, upboard_gpio_request() was not yet called, so the driver didn't + * request the corresponding SoC pin. So the SoC pin is probably in function (not in + * GPIO mode). + * + * To get the direction of the SoC pin, it shall be requested in GPIO mode. + * Once a SoC pin is set in GPIO mode, there is no way to set it back to its + * function mode. + * Instead of returning the SoC pin direction, the direction of the FPGA pin is + * returned (only for the get_direction() called during the gpio_chip registration). + */ + mode = upboard_pinctrl_pin_get_mode(pctrl->pctldev, pin); + + /* If the pin is in function mode or high-z, input direction is returned */ + if (mode < 0) + return mode; + if (mode == UPBOARD_PIN_MODE_GPIO_OUT) + return GPIO_LINE_DIRECTION_OUT; + else + return GPIO_LINE_DIRECTION_IN; +} + +static int upboard_gpio_get(struct gpio_chip *gc, unsigned int offset) +{ + struct upboard_pinctrl *pctrl = container_of(gc, struct upboard_pinctrl, chip); + + return gpiod_get_value(pctrl->gpio[offset]); +} + +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); + + gpiod_set_value(pctrl->gpio[offset], value); +} + +static int upboard_gpio_direction_input(struct gpio_chip *gc, unsigned int offset) +{ + struct upboard_pinctrl *pctrl = container_of(gc, struct upboard_pinctrl, chip); + int ret; + + ret = pinctrl_gpio_direction_input(gc, offset); + if (ret) + return ret; + + return gpiod_direction_input(pctrl->gpio[offset]); +} + +static int upboard_gpio_direction_output(struct gpio_chip *gc, unsigned int offset, int value) +{ + struct upboard_pinctrl *pctrl = container_of(gc, struct upboard_pinctrl, chip); + int ret; + + ret = pinctrl_gpio_direction_output(gc, offset); + if (ret) + return ret; + + return gpiod_direction_output(pctrl->gpio[offset], value); +} + +static int upboard_gpio_to_irq(struct gpio_chip *gc, unsigned int offset) +{ + struct upboard_pinctrl *pctrl = container_of(gc, struct upboard_pinctrl, chip); + + return gpiod_to_irq(pctrl->gpio[offset]); +} + +static int upboard_pinctrl_register_groups(struct upboard_pinctrl *pctrl) +{ + const struct upboard_pingroup *groups = pctrl->pctrl_data->groups; + size_t ngroups = pctrl->pctrl_data->ngroups; + int ret, i; + + for (i = 0; i < ngroups; i++) { + ret = pinctrl_generic_add_group(pctrl->pctldev, groups[i].grp.name, + groups[i].grp.pins, groups[i].grp.npins, pctrl); + if (ret < 0) + return ret; + } + + return 0; +} + +static int upboard_pinctrl_register_functions(struct upboard_pinctrl *pctrl) +{ + const struct pinfunction *funcs = pctrl->pctrl_data->funcs; + size_t nfuncs = pctrl->pctrl_data->nfuncs; + int ret, i; + + for (i = 0; i < nfuncs ; ++i) { + ret = pinmux_generic_add_function(pctrl->pctldev, funcs[i].name, funcs[i].groups, + funcs[i].ngroups, NULL); + if (ret < 0) + return ret; + } + + return 0; +} + +static int upboard_pinctrl_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct upboard_fpga *fpga = dev_get_drvdata(dev->parent); + struct pinctrl_desc *pctldesc; + struct upboard_pinctrl *pctrl; + struct upboard_pin *pins; + struct gpio_chip *chip; + int ret, i; + + pctldesc = devm_kzalloc(dev, sizeof(*pctldesc), GFP_KERNEL); + if (!pctldesc) + return -ENOMEM; + + pctrl = devm_kzalloc(dev, sizeof(*pctrl), GFP_KERNEL); + if (!pctrl) + return -ENOMEM; + + switch (fpga->fpga_data->type) { + case UPBOARD_UP_FPGA: + pctldesc->pins = upboard_up_pins; + pctldesc->npins = ARRAY_SIZE(upboard_up_pins); + pctrl->pctrl_data = &upboard_up_pinctrl_data; + break; + case UPBOARD_UP2_FPGA: + pctldesc->pins = upboard_up2_pins; + pctldesc->npins = ARRAY_SIZE(upboard_up2_pins); + pctrl->pctrl_data = &upboard_up2_pinctrl_data; + break; + default: + return dev_err_probe(dev, -ENODEV, "Unsupported device type %d\n", + fpga->fpga_data->type); + } + + pctldesc->name = dev_name(dev); + pctldesc->owner = THIS_MODULE; + pctldesc->pctlops = &upboard_pinctrl_ops; + pctldesc->pmxops = &upboard_pinmux_ops; + + pctrl->dev = dev; + + pins = devm_kcalloc(dev, pctldesc->npins, sizeof(*pins), GFP_KERNEL); + if (!pins) + return -ENOMEM; + + /* Initialize pins */ + for (i = 0; i < pctldesc->npins; i++) { + const struct pinctrl_pin_desc *pin_desc = &pctldesc->pins[i]; + unsigned int regoff = pin_desc->number / UPBOARD_REGISTER_SIZE; + unsigned int lsb = pin_desc->number % UPBOARD_REGISTER_SIZE; + struct reg_field * const fld_func = pin_desc->drv_data; + struct upboard_pin *pin = &pins[i]; + struct reg_field fldconf = {}; + + if (fld_func) { + pin->funcbit = devm_regmap_field_alloc(dev, fpga->regmap, *fld_func); + + if (IS_ERR(pin->funcbit)) + return PTR_ERR(pin->funcbit); + } + + fldconf.reg = UPBOARD_REG_GPIO_EN0 + regoff; + fldconf.lsb = lsb; + fldconf.msb = lsb; + pin->enbit = devm_regmap_field_alloc(dev, fpga->regmap, fldconf); + if (IS_ERR(pin->enbit)) + return PTR_ERR(pin->enbit); + + fldconf.reg = UPBOARD_REG_GPIO_DIR0 + regoff; + fldconf.lsb = lsb; + fldconf.msb = lsb; + pin->dirbit = devm_regmap_field_alloc(dev, fpga->regmap, fldconf); + if (IS_ERR(pin->dirbit)) + return PTR_ERR(pin->dirbit); + } + + pctrl->pins = pins; + + ret = pinctrl_register_mappings(pctrl->pctrl_data->maps, pctrl->pctrl_data->nmaps); + + ret = devm_pinctrl_register_and_init(dev, pctldesc, pctrl, &pctrl->pctldev); + if (ret) + return ret; + + ret = upboard_pinctrl_register_groups(pctrl); + if (ret) + return dev_err_probe(dev, ret, "Failed to register groups"); + + ret = upboard_pinctrl_register_functions(pctrl); + if (ret) + return dev_err_probe(dev, ret, "Failed to register functions"); + + ret = pinctrl_enable(pctrl->pctldev); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable pinctrl"); + + chip = &pctrl->chip; + chip->owner = THIS_MODULE; + chip->label = dev_name(&pdev->dev); + chip->parent = &pdev->dev; + chip->ngpio = pctrl->pctrl_data->ngpio; + chip->base = -1; + chip->request = upboard_gpio_request; + chip->free = upboard_gpio_free; + chip->get = upboard_gpio_get; + chip->set = upboard_gpio_set; + chip->get_direction = upboard_gpio_get_direction; + chip->direction_input = upboard_gpio_direction_input; + chip->direction_output = upboard_gpio_direction_output; + chip->to_irq = upboard_gpio_to_irq; + + pctrl->gpio = devm_kcalloc(dev, pctrl->pctrl_data->ngpio, sizeof(*pctrl->gpio), GFP_KERNEL); + if (!pctrl->gpio) + return -ENOMEM; + + ret = devm_gpiochip_add_data(&pdev->dev, chip, pctrl); + if (ret) + return ret; + + ret = gpiochip_add_pinlist_range(chip, dev_name(dev), 0, pctrl->pctrl_data->pin_header, + pctrl->pctrl_data->ngpio); + if (ret) + gpiochip_remove(chip); + + return ret; +} + +static struct platform_driver upboard_pinctrl_driver = { + .driver = { + .name = "upboard-pinctrl", + }, + .probe = upboard_pinctrl_probe, +}; + +module_platform_driver(upboard_pinctrl_driver); + +MODULE_AUTHOR("Gary Wang "); +MODULE_AUTHOR("Thomas Richard X-Patchwork-Id: 849443 Received: from relay1-d.mail.gandi.net (relay1-d.mail.gandi.net [217.70.183.193]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id AC7BD1DD0F6; Wed, 11 Dec 2024 16:28:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.70.183.193 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1733934482; cv=none; b=o6eVhWbzv3DvaQfJXRwjzotllHQSdr+ORbTdBG9FPnmS3z92amENDZI10mqZl9FTJzD00kz40YXeyH18YAwowKlql01EfhqffbmI+v2bYc15wd9X04maPzQTeOpKMVFQADwLFjMKYsC0WSEtFnbWK3rmAb0piwSoTXtsrKC73W0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1733934482; c=relaxed/simple; bh=Uvt+xO4mjtJ/nsz5b3dWBGZ1QttcbioYU/hdTDGFLG4=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=s776kSJE5sdlAv5alqTqyKzw4rJ9qB7Do/3YdkzdlGSkEiFx+2MFgP3gu20jlO/zs9hsxFOdHY8ADz+B5pD0bcSalFqoEi16Zq8cEhjR3LH6uc6z+e5A3EvGgMfgtyIbGgBJBWjcCOYi2TGOWlnv2uJI8HpeCWC32WaXwR/7HqM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com; spf=pass smtp.mailfrom=bootlin.com; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b=RP6DwWAa; arc=none smtp.client-ip=217.70.183.193 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=bootlin.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b="RP6DwWAa" Received: by mail.gandi.net (Postfix) with ESMTPSA id 2B6FC240008; Wed, 11 Dec 2024 16:27:53 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1733934473; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=AViKE+IyOFawE9/8u97WTB205yA2yY18fxRqJGUc4KQ=; b=RP6DwWAaEl7ycWuw2h1bIS/OLTIOs6jvswGnPDsmEM6QyJKyn2/UD1ZEw3SP8Ft0g5plES v6r0Jb7oA/j8xhzxVVu0V9z8c0o7oHSmISr6NUSKdpSJ8hN98yYNAOT/8/wy9HdEyeD1BN 0IqYpbw22BYaPaOKt7G60ppeZ4jN4dl/bEnnTOBQcxVTxbUMbtH7kZZgsI5+tiozntJ2I6 WExfwjEBoWJT58e0bwW8yxvNMLkBaQiz4Q9MeQ88Yl4fmyb2f3pWLZeahzD612DtvjLZg5 Dql1unJNJHzsqWCAnLDL2/FTgigtWoOgiK4HjE/Ogq/lgsZI/v+pKUgsyVjAwA== From: Thomas Richard Date: Wed, 11 Dec 2024 17:27:20 +0100 Subject: [PATCH 5/5] MAINTAINERS: Add entry for AAEON UP board FPGA drivers Precedence: bulk X-Mailing-List: linux-gpio@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20241211-aaeon-up-board-pinctrl-support-v1-5-24719be27631@bootlin.com> References: <20241211-aaeon-up-board-pinctrl-support-v1-0-24719be27631@bootlin.com> In-Reply-To: <20241211-aaeon-up-board-pinctrl-support-v1-0-24719be27631@bootlin.com> To: Linus Walleij , Bartosz Golaszewski , Lee Jones , Pavel Machek Cc: linux-gpio@vger.kernel.org, linux-kernel@vger.kernel.org, linux-leds@vger.kernel.org, thomas.petazzoni@bootlin.com, DanieleCleri@aaeon.eu, GaryWang@aaeon.com.tw, Thomas Richard X-Mailer: b4 0.14.1 X-GND-Sasl: thomas.richard@bootlin.com Add the AAEON UP board FPGA drivers and header as Maintained by myself. Signed-off-by: Thomas Richard --- MAINTAINERS | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 1e930c7a58b1..cbe4cfbe110d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -185,6 +185,14 @@ W: http://www.adaptec.com/ F: Documentation/scsi/aacraid.rst F: drivers/scsi/aacraid/ +AAEON UPBOARD FPGA MFD DRIVER +M: Thomas Richard +S: Maintained +F: drivers/leds/leds-upboard.c +F: drivers/mfd/upboard-fpga.c +F: drivers/pinctrl/pinctrl-upboard.c +F: include/linux/mfd/upboard-fpga.h + AB8500 BATTERY AND CHARGER DRIVERS M: Linus Walleij F: Documentation/devicetree/bindings/power/supply/*ab8500*