2022-10-01 09:12:47

by chengwei

[permalink] [raw]
Subject: [PATCH] mfd: Add support for UP board CPLD/FPGA

The UP Squared board <http://www.upboard.com> implements certain
features (pin control, onboard LEDs or CEC) through an on-board 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 come with a few FPGA-controlled onboard LEDs:
* UP Board: yellow, green, red
* UP Squared: blue, yellow, green, red

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 <[email protected]>
[merge various fixes]
Signed-off-by: Nicola Lunghi <[email protected]>
Signed-off-by: chengwei <[email protected]>
---
drivers/leds/Kconfig | 10 +
drivers/leds/Makefile | 1 +
drivers/leds/leds-upboard.c | 90 ++++++
drivers/mfd/Kconfig | 9 +
drivers/mfd/Makefile | 1 +
drivers/mfd/upboard-fpga.c | 486 +++++++++++++++++++++++++++++++
include/linux/mfd/upboard-fpga.h | 53 ++++
7 files changed, 650 insertions(+)
create mode 100644 drivers/leds/leds-upboard.c
create mode 100644 drivers/mfd/upboard-fpga.c
create mode 100644 include/linux/mfd/upboard-fpga.h

diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 499d0f215a8b..34337b2b37f4 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_UPBOARD_FPGA
+ help
+ This option enables support for the UP board LEDs. The UP boards come
+ with a few FPGA-controlled onboard LEDs. The UP Squared includes 4 LEDs
+ (yellow, green, red and blue) on the underside of the board which are
+ controlled by the pin control FPGA on the board
+
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..5128c5ff7ec2
--- /dev/null
+++ b/drivers/leds/leds-upboard.c
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * UP Board FPGA-based LED driver
+ *
+ * Copyright (c) 2017, Emutex Ltd. All rights reserved.
+ *
+ * Author: Javier Arteaga <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#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 regmap_field *field;
+ struct led_classdev cdev;
+};
+
+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 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;
+
+ if (!fpga || !pdata)
+ return -EINVAL;
+
+ 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, 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 <[email protected]>");
+MODULE_DESCRIPTION("UP Board LED driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:upboard-led");
+
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index abb58ab1a1a4..bb4e8f04240b 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -2104,6 +2104,15 @@ 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_UPBOARD_FPGA
+ tristate "Support for the UP board FPGA"
+ select MFD_CORE
+ help
+ Select this option to enable the UP and UP^2 on-board FPGA. The UP
+ board implements certain features (pin control, onboard LEDs or CEC)
+ through an on-board FPGA. This mfd driver implements the line protocol
+ to read and write registers from the FPGA through regmap.
+
menu "Multimedia Capabilities Port drivers"
depends on ARCH_SA1100

diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 858cacf659d6..d9d10e3664f7 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_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..04be25931e25
--- /dev/null
+++ b/drivers/mfd/upboard-fpga.c
@@ -0,0 +1,486 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * UP Board main platform driver and FPGA configuration support
+ *
+ * Copyright (c) 2017, Emutex Ltd. All rights reserved.
+ *
+ * Author: Javier Arteaga <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#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>
+
+static int upboard_fpga_read(void *, unsigned int, unsigned int *);
+static int upboard_fpga_write(void *, unsigned int, unsigned int);
+
+struct upboard_fpga_data {
+ const struct regmap_config *regmapconf;
+ const struct mfd_cell *cells;
+ size_t ncells;
+};
+
+#define UPBOARD_LED_CELL(led_data, n) \
+ { \
+ .name = "upboard-led", \
+ .id = (n), \
+ .platform_data = &led_data[(n)], \
+ .pdata_size = sizeof(*(led_data)), \
+ }
+
+/* UP board */
+
+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" },
+ UPBOARD_LED_CELL(upboard_up_led_data, 0),
+ UPBOARD_LED_CELL(upboard_up_led_data, 1),
+ UPBOARD_LED_CELL(upboard_up_led_data, 2),
+};
+
+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),
+};
+
+/* 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_RBTREE,
+ .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" },
+ UPBOARD_LED_CELL(upboard_up2_led_data, 0),
+ UPBOARD_LED_CELL(upboard_up2_led_data, 1),
+ UPBOARD_LED_CELL(upboard_up2_led_data, 2),
+ UPBOARD_LED_CELL(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-CREX carrier board for UP Core */
+
+/* same MAXV config as UP1 (proto2 release) */
+#define upboard_upcore_crex_fpga_data upboard_up_fpga_data
+
+/* 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 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;
+};
+
+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 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.
+ */
+ 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;
+}
+
+static int __init upboard_fpga_detect_firmware(struct upboard_fpga *fpga)
+{
+ const unsigned int AAEON_MANUFACTURER_ID = 0x01;
+ const unsigned int SUPPORTED_FW_MAJOR = 0x0;
+ unsigned int platform_id, manufacturer_id;
+ unsigned int firmware_id, build, major, minor, patch;
+ int ret;
+
+ ret = regmap_read(fpga->regmap, UPFPGA_REG_PLATFORM_ID, &platform_id);
+ if (ret)
+ return ret;
+
+ manufacturer_id = platform_id & 0xff;
+ if (manufacturer_id != AAEON_MANUFACTURER_ID) {
+ dev_dbg(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 >> 12) & 0xf;
+ major = (firmware_id >> 8) & 0xf;
+ minor = (firmware_id >> 4) & 0xf;
+ patch = firmware_id & 0xf;
+ if (major != SUPPORTED_FW_MAJOR) {
+ dev_dbg(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;
+}
+
+static const struct acpi_device_id upboard_fpga_acpi_match[] = {
+ { "AANT0F00", (kernel_ulong_t)&upboard_up_fpga_data },
+ { "AANT0F01", (kernel_ulong_t)&upboard_up2_fpga_data },
+ { "AANT0F02", (kernel_ulong_t)&upboard_upcore_crex_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);
+
+#define UPFPGA_QUIRK_UNINITIALISED BIT(0)
+#define UPFPGA_QUIRK_HRV1_IS_PROTO2 BIT(1)
+#define UPFPGA_QUIRK_GPIO_LED BIT(2)
+
+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 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 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),
+ },
+ { },
+};
+
+#define UPFPGA_PROTOCOL_V2_HRV 2
+
+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;
+ acpi_handle handle;
+ acpi_status status;
+ unsigned long long hrv;
+ unsigned long quirks = 0;
+ int ret;
+
+ id = acpi_match_device(upboard_fpga_acpi_match, &pdev->dev);
+ if (!id)
+ return -ENODEV;
+
+ handle = ACPI_HANDLE(&pdev->dev);
+ status = acpi_evaluate_integer(handle, "_HRV", NULL, &hrv);
+ if (ACPI_FAILURE(status)) {
+ dev_err(&pdev->dev, "failed to get PCTL revision");
+ return -ENODEV;
+ }
+
+ system_id = dmi_first_match(upboard_dmi_table);
+ if (system_id)
+ quirks = (unsigned long)system_id->driver_data;
+
+ if (hrv == 1 && (quirks & UPFPGA_QUIRK_HRV1_IS_PROTO2))
+ hrv = UPFPGA_PROTOCOL_V2_HRV;
+
+ if (hrv != UPFPGA_PROTOCOL_V2_HRV) {
+ dev_dbg(&pdev->dev, "unsupported PCTL revision: %llu", hrv);
+ return -ENODEV;
+ }
+
+ 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);
+ if (IS_ERR(fpga->regmap))
+ return PTR_ERR(fpga->regmap);
+
+ ret = upboard_fpga_gpio_init(fpga);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to init FPGA comm GPIOs: %d", ret);
+ return ret;
+ }
+
+ ret = upboard_fpga_detect_firmware(fpga);
+ if (ret)
+ return ret;
+
+ if (quirks & UPFPGA_QUIRK_GPIO_LED) {
+#define APL_GPIO_218 507
+ 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[] = {
+ {
+ .name = "leds-gpio",
+ .id = 0,
+ .platform_data = &upboard_gpio_led_platform_data,
+ .pdata_size = sizeof(upboard_gpio_led_platform_data),
+ },
+ };
+
+ 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("Javier Arteaga <[email protected]>");
+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..47ef4c5b1976
--- /dev/null
+++ b/include/linux/mfd/upboard-fpga.h
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * UP Board FPGA MFD driver interface
+ *
+ * Copyright (c) 2017, Emutex Ltd. All rights reserved.
+ *
+ * Author: Javier Arteaga <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __LINUX_MFD_UPBOARD_FPGA_H
+#define __LINUX_MFD_UPBOARD_FPGA_H
+
+#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;
+ 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;
+};
+
+#endif /* __LINUX_MFD_UPBOARD_FPGA_H */
--
2.17.1


2022-10-01 13:30:10

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH] mfd: Add support for UP board CPLD/FPGA

Hi chengwei,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on pavel-leds/for-next]
[also build test ERROR on lee-mfd/for-mfd-next linus/master v6.0-rc7 next-20220930]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url: https://github.com/intel-lab-lkp/linux/commits/chengwei/mfd-Add-support-for-UP-board-CPLD-FPGA/20221001-170758
base: git://git.kernel.org/pub/scm/linux/kernel/git/pavel/linux-leds.git for-next
config: sh-allmodconfig
compiler: sh4-linux-gcc (GCC) 12.1.0
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# https://github.com/intel-lab-lkp/linux/commit/6d967fb7e06c0b284bf19de7680621c4d78348eb
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review chengwei/mfd-Add-support-for-UP-board-CPLD-FPGA/20221001-170758
git checkout 6d967fb7e06c0b284bf19de7680621c4d78348eb
# save the config file
mkdir build_dir && cp config build_dir/.config
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=sh SHELL=/bin/bash drivers/

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <[email protected]>

All errors (new ones prefixed by >>):

drivers/mfd/upboard-fpga.c: In function 'upboard_fpga_probe':
>> drivers/mfd/upboard-fpga.c:394:18: error: implicit declaration of function 'acpi_evaluate_integer'; did you mean 'acpi_evaluate_object'? [-Werror=implicit-function-declaration]
394 | status = acpi_evaluate_integer(handle, "_HRV", NULL, &hrv);
| ^~~~~~~~~~~~~~~~~~~~~
| acpi_evaluate_object
cc1: some warnings being treated as errors


vim +394 drivers/mfd/upboard-fpga.c

376
377 static int __init upboard_fpga_probe(struct platform_device *pdev)
378 {
379 struct upboard_fpga *fpga;
380 const struct acpi_device_id *id;
381 const struct upboard_fpga_data *fpga_data;
382 const struct dmi_system_id *system_id;
383 acpi_handle handle;
384 acpi_status status;
385 unsigned long long hrv;
386 unsigned long quirks = 0;
387 int ret;
388
389 id = acpi_match_device(upboard_fpga_acpi_match, &pdev->dev);
390 if (!id)
391 return -ENODEV;
392
393 handle = ACPI_HANDLE(&pdev->dev);
> 394 status = acpi_evaluate_integer(handle, "_HRV", NULL, &hrv);
395 if (ACPI_FAILURE(status)) {
396 dev_err(&pdev->dev, "failed to get PCTL revision");
397 return -ENODEV;
398 }
399
400 system_id = dmi_first_match(upboard_dmi_table);
401 if (system_id)
402 quirks = (unsigned long)system_id->driver_data;
403
404 if (hrv == 1 && (quirks & UPFPGA_QUIRK_HRV1_IS_PROTO2))
405 hrv = UPFPGA_PROTOCOL_V2_HRV;
406
407 if (hrv != UPFPGA_PROTOCOL_V2_HRV) {
408 dev_dbg(&pdev->dev, "unsupported PCTL revision: %llu", hrv);
409 return -ENODEV;
410 }
411
412 fpga_data = (const struct upboard_fpga_data *) id->driver_data;
413
414 fpga = devm_kzalloc(&pdev->dev, sizeof(*fpga), GFP_KERNEL);
415 if (!fpga)
416 return -ENOMEM;
417
418 if (quirks & UPFPGA_QUIRK_UNINITIALISED) {
419 dev_info(&pdev->dev, "FPGA not initialised by this BIOS");
420 fpga->uninitialised = true;
421 }
422
423 dev_set_drvdata(&pdev->dev, fpga);
424 fpga->dev = &pdev->dev;
425 fpga->regmap = devm_regmap_init(&pdev->dev, NULL, fpga,
426 fpga_data->regmapconf);
427 if (IS_ERR(fpga->regmap))
428 return PTR_ERR(fpga->regmap);
429
430 ret = upboard_fpga_gpio_init(fpga);
431 if (ret) {
432 dev_err(&pdev->dev, "failed to init FPGA comm GPIOs: %d", ret);
433 return ret;
434 }
435
436 ret = upboard_fpga_detect_firmware(fpga);
437 if (ret)
438 return ret;
439
440 if (quirks & UPFPGA_QUIRK_GPIO_LED) {
441 #define APL_GPIO_218 507
442 static struct gpio_led upboard_gpio_leds[] = {
443 {
444 .name = "upboard:blue:",
445 .gpio = APL_GPIO_218,
446 .default_state = LEDS_GPIO_DEFSTATE_KEEP,
447 },
448 };
449 static struct gpio_led_platform_data upboard_gpio_led_platform_data = {
450 .num_leds = ARRAY_SIZE(upboard_gpio_leds),
451 .leds = upboard_gpio_leds,
452 };
453 static const struct mfd_cell upboard_gpio_led_cells[] = {
454 {
455 .name = "leds-gpio",
456 .id = 0,
457 .platform_data = &upboard_gpio_led_platform_data,
458 .pdata_size = sizeof(upboard_gpio_led_platform_data),
459 },
460 };
461
462 ret = devm_mfd_add_devices(&pdev->dev, 0, upboard_gpio_led_cells,
463 ARRAY_SIZE(upboard_gpio_led_cells), NULL, 0, NULL);
464 if (ret) {
465 dev_err(&pdev->dev, "Failed to add GPIO leds");
466 return ret;
467 }
468
469 }
470
471 return devm_mfd_add_devices(&pdev->dev, 0, fpga_data->cells,
472 fpga_data->ncells, NULL, 0, NULL);
473 }
474

--
0-DAY CI Kernel Test Service
https://01.org/lkp


Attachments:
(No filename) (5.39 kB)
config (246.75 kB)
Download all attachments

2022-10-03 08:40:15

by Lee Jones

[permalink] [raw]
Subject: Re: [PATCH] mfd: Add support for UP board CPLD/FPGA

On Sat, 01 Oct 2022, chengwei wrote:

> The UP Squared board <http://www.upboard.com> implements certain
> features (pin control, onboard LEDs or CEC) through an on-board 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 come with a few FPGA-controlled onboard LEDs:
> * UP Board: yellow, green, red
> * UP Squared: blue, yellow, green, red
>
> 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 <[email protected]>
> [merge various fixes]
> Signed-off-by: Nicola Lunghi <[email protected]>
> Signed-off-by: chengwei <[email protected]>
> ---
> drivers/leds/Kconfig | 10 +
> drivers/leds/Makefile | 1 +
> drivers/leds/leds-upboard.c | 90 ++++++
> drivers/mfd/Kconfig | 9 +
> drivers/mfd/Makefile | 1 +
> drivers/mfd/upboard-fpga.c | 486 +++++++++++++++++++++++++++++++
> include/linux/mfd/upboard-fpga.h | 53 ++++

Please separate the LED and MFD changes into their own patches.

> 7 files changed, 650 insertions(+)
> create mode 100644 drivers/leds/leds-upboard.c
> create mode 100644 drivers/mfd/upboard-fpga.c
> create mode 100644 include/linux/mfd/upboard-fpga.h

--
Lee Jones [李琼斯]

2022-10-03 12:24:44

by Andy Shevchenko

[permalink] [raw]
Subject: Re: [PATCH] mfd: Add support for UP board CPLD/FPGA

On Sat, Oct 01, 2022 at 05:05:47PM +0800, chengwei wrote:
> The UP Squared board <http://www.upboard.com> implements certain
> features (pin control, onboard LEDs or CEC) through an on-board 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 come with a few FPGA-controlled onboard LEDs:
> * UP Board: yellow, green, red
> * UP Squared: blue, yellow, green, red
>
> 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

...

> +config LEDS_UPBOARD
> + tristate "LED support for the UP board"
> + depends on LEDS_CLASS
> + depends on MFD_UPBOARD_FPGA
> + help
> + This option enables support for the UP board LEDs. The UP boards come
> + with a few FPGA-controlled onboard LEDs. The UP Squared includes 4 LEDs
> + (yellow, green, red and blue) on the underside of the board which are
> + controlled by the pin control FPGA on the board

What would be the module name?

Also take care in every comment and text of the English grammar and
punctuation. (Here, for example, the trailing period is missed)

...

> +// SPDX-License-Identifier: GPL-2.0-only

> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.

This text is redundant as far as SPDX is provided.

...

> +#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>

Instead of implying the headers by kernel.h, please revisit this list and add
what you are using (like container_of.h, types.h, etc).

...

> +struct upboard_led {
> + struct regmap_field *field;
> + struct led_classdev cdev;
> +};

If you put cdev as a first member it may decrease a code size, but check this
with bloat-o-meter.

...

> +static enum led_brightness upboard_led_brightness_get(struct led_classdev
> + *cdev)

Wrong indentation. Why not to put on one line?
Same Q to other similar places.

...

> +static int __init upboard_led_probe(struct platform_device *pdev)

__init ?! Haven't your linker issued a warning about section mismatches?

...

> + struct upboard_fpga * const fpga = dev_get_drvdata(pdev->dev.parent);

fpga is a confusing name since we have the fpga framework in the Linux kernel.
I believe you are talking about cpld, which suits here better.

...

> + struct upboard_led_data * const pdata = pdev->dev.platform_data;

dev_get_platdata()

...

> + if (!fpga || !pdata)
> + return -EINVAL;

When is this true?

...

> + led->cdev.name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "upboard:%s:",
> + pdata->colour);

> +

Redundant blank line.

> + if (!led->cdev.name)
> + return -ENOMEM;

...

> +static struct platform_driver upboard_led_driver = {
> + .driver = {
> + .name = "upboard-led",
> + },
> +};

> +

Ditto.

> +module_platform_driver_probe(upboard_led_driver, upboard_led_probe);

> +MODULE_AUTHOR("Javier Arteaga <[email protected]>");
> +MODULE_DESCRIPTION("UP Board LED driver");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:upboard-led");

> +

Why is this?

...

> +config MFD_UPBOARD_FPGA
> + tristate "Support for the UP board FPGA"
> + select MFD_CORE
> + help
> + Select this option to enable the UP and UP^2 on-board FPGA. The UP
> + board implements certain features (pin control, onboard LEDs or CEC)
> + through an on-board FPGA. This mfd driver implements the line protocol
> + to read and write registers from the FPGA through regmap.

Module name?

...

And seems so on as per LED driver...

I stop my review here. This submission needs more work.

--
With Best Regards,
Andy Shevchenko