All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/2] gpio: Add Turris Omnia MCU driver
@ 2022-07-25 13:01 Pali Rohár
  2022-07-25 13:01 ` [PATCH 2/2] arm: mvebu: turris_omnia: Add mcu node with gpio-controller Pali Rohár
                   ` (2 more replies)
  0 siblings, 3 replies; 10+ messages in thread
From: Pali Rohár @ 2022-07-25 13:01 UTC (permalink / raw)
  To: Stefan Roese; +Cc: Marek Behun, u-boot

This driver registers GPIO controller and allows U-Boot to control GPIO
pins on MCU which is connected to Turris Omnia via i2c.

Signed-off-by: Pali Rohár <pali@kernel.org>
---
 board/CZ.NIC/turris_omnia/MAINTAINERS |   1 +
 drivers/gpio/Kconfig                  |   7 +
 drivers/gpio/Makefile                 |   1 +
 drivers/gpio/turris_omnia_mcu.c       | 314 ++++++++++++++++++++++++++
 4 files changed, 323 insertions(+)
 create mode 100644 drivers/gpio/turris_omnia_mcu.c

diff --git a/board/CZ.NIC/turris_omnia/MAINTAINERS b/board/CZ.NIC/turris_omnia/MAINTAINERS
index 8258f4fc5b23..8bff97cc2737 100644
--- a/board/CZ.NIC/turris_omnia/MAINTAINERS
+++ b/board/CZ.NIC/turris_omnia/MAINTAINERS
@@ -6,4 +6,5 @@ F:	arch/arm/dts/armada-385-turris-omnia*.dts*
 F:	board/CZ.NIC/turris_atsha_otp.*
 F:	board/CZ.NIC/turris_omnia/
 F:	configs/turris_omnia_defconfig
+F:	drivers/gpio/turris_omnia_mcu.c
 F:	include/configs/turris_omnia.h
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index aaa152fae73b..82a8bca27000 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -598,4 +598,11 @@ config SLG7XL45106_I2C_GPO
 	   8-bit gpo expander, all gpo lines are controlled by writing
 	   value into data register.
 
+config TURRIS_OMNIA_MCU
+	bool "Turris Omnia MCU GPIO driver"
+	depends on DM_GPIO
+	default y if TARGET_TURRIS_OMNIA
+	help
+	   Support for GPIOs on MCU connected to Turris Omnia via i2c.
+
 endif
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index d7552762d065..219f37e0e434 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -75,3 +75,4 @@ obj-$(CONFIG_MAX7320_GPIO)	+= max7320_gpio.o
 obj-$(CONFIG_SL28CPLD_GPIO)	+= sl28cpld-gpio.o
 obj-$(CONFIG_ZYNQMP_GPIO_MODEPIN)	+= zynqmp_gpio_modepin.o
 obj-$(CONFIG_SLG7XL45106_I2C_GPO)	+= gpio_slg7xl45106.o
+obj-$(CONFIG_$(SPL_TPL_)TURRIS_OMNIA_MCU)	+= turris_omnia_mcu.o
diff --git a/drivers/gpio/turris_omnia_mcu.c b/drivers/gpio/turris_omnia_mcu.c
new file mode 100644
index 000000000000..44a6574d5f17
--- /dev/null
+++ b/drivers/gpio/turris_omnia_mcu.c
@@ -0,0 +1,314 @@
+// SPDX-License-Identifier: GPL-2.0+
+// (C) 2022 Pali Rohár <pali@kernel.org>
+
+#include <common.h>
+#include <dm.h>
+#include <i2c.h>
+#include <asm/gpio.h>
+#include <linux/log2.h>
+
+enum commands_e {
+	CMD_GET_STATUS_WORD                 = 0x01,
+	CMD_GENERAL_CONTROL                 = 0x02,
+
+	/* available if STS_FEATURES_SUPPORTED bit set in status word */
+	CMD_GET_FEATURES                    = 0x10,
+
+	/* available if FEAT_EXT_CMDS bit is set in features */
+	CMD_GET_EXT_STATUS_DWORD            = 0x11,
+	CMD_EXT_CONTROL                     = 0x12,
+	CMD_GET_EXT_CONTROL_STATUS          = 0x13,
+};
+
+/* CMD_GET_STATUS_WORD */
+enum sts_word_e {
+	STS_MCU_TYPE_MASK                = GENMASK(1, 0),
+	STS_MCU_TYPE_STM32               = 0,
+	STS_MCU_TYPE_GD32                = 1,
+	STS_MCU_TYPE_MKL                 = 2,
+	STS_FEATURES_SUPPORTED           = BIT(2),
+	STS_USER_REGULATOR_NOT_SUPPORTED = BIT(3),
+	STS_CARD_DET                     = BIT(4),
+	STS_MSATA_IND                    = BIT(5),
+	STS_USB30_OVC                    = BIT(6),
+	STS_USB31_OVC                    = BIT(7),
+	STS_USB30_PWRON                  = BIT(8),
+	STS_USB31_PWRON                  = BIT(9),
+	STS_ENABLE_4V5                   = BIT(10),
+	STS_BUTTON_MODE                  = BIT(11),
+	STS_BUTTON_PRESSED               = BIT(12),
+	STS_BUTTON_COUNTER_MASK          = GENMASK(15, 13)
+};
+
+/* CMD_GENERAL_CONTROL */
+enum ctl_byte_e {
+	CTL_LIGHT_RST   = BIT(0),
+	CTL_HARD_RST    = BIT(1),
+	/*CTL_RESERVED    = BIT(2),*/
+	CTL_USB30_PWRON = BIT(3),
+	CTL_USB31_PWRON = BIT(4),
+	CTL_ENABLE_4V5  = BIT(5),
+	CTL_BUTTON_MODE = BIT(6),
+	CTL_BOOTLOADER  = BIT(7)
+};
+
+/* CMD_GET_FEATURES */
+enum features_e {
+	FEAT_EXT_CMDS           = BIT(1),
+};
+
+struct turris_omnia_mcu_info {
+	u16 features;
+};
+
+static int turris_omnia_mcu_get_function(struct udevice *dev, uint offset)
+{
+	struct turris_omnia_mcu_info *info = dev_get_plat(dev);
+
+	switch (offset) {
+	/* bank 0 */
+	case 0 ... 15:
+		switch (offset) {
+		case ilog2(STS_USB30_PWRON):
+		case ilog2(STS_USB31_PWRON):
+		case ilog2(STS_ENABLE_4V5):
+		case ilog2(STS_BUTTON_MODE):
+			return GPIOF_OUTPUT;
+		default:
+			return GPIOF_INPUT;
+		}
+
+	/* bank 1 - supported only when FEAT_EXT_CMDS is set */
+	case (16 + 0) ... (16 + 31):
+		if (!(info->features & FEAT_EXT_CMDS))
+			return -EINVAL;
+		return GPIOF_INPUT;
+
+	/* bank 2 - supported only when FEAT_EXT_CMDS is set */
+	case (16 + 32 + 0) ... (16 + 32 + 15):
+		if (!(info->features & FEAT_EXT_CMDS))
+			return -EINVAL;
+		return GPIOF_OUTPUT;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static int turris_omnia_mcu_get_value(struct udevice *dev, uint offset)
+{
+	struct turris_omnia_mcu_info *info = dev_get_plat(dev);
+	u8 val16[2];
+	u8 val32[4];
+	int ret;
+
+	switch (offset) {
+	/* bank 0 */
+	case 0 ... 15:
+		ret = dm_i2c_read(dev, CMD_GET_STATUS_WORD, val16, 2);
+		if (ret)
+			return ret;
+		return ((((u16)val16[1] << 8) | val16[0]) >> offset) & 0x1;
+
+	/* bank 1 - supported only when FEAT_EXT_CMDS is set */
+	case (16 + 0) ... (16 + 31):
+		if (!(info->features & FEAT_EXT_CMDS))
+			return -EINVAL;
+		ret = dm_i2c_read(dev, CMD_GET_EXT_STATUS_DWORD, val32, 4);
+		if (ret)
+			return ret;
+		return ((((u32)val32[3] << 24) | ((u32)val32[2] << 16) |
+			 ((u32)val32[1] << 8) | val32[0]) >> (offset - 16)) & 0x1;
+
+	/* bank 2 - supported only when FEAT_EXT_CMDS is set */
+	case (16 + 32 + 0) ... (16 + 32 + 15):
+		if (!(info->features & FEAT_EXT_CMDS))
+			return -EINVAL;
+		ret = dm_i2c_read(dev, CMD_GET_EXT_CONTROL_STATUS, val16, 2);
+		if (ret)
+			return ret;
+		return ((((u16)val16[1] << 8) | val16[0]) >> (offset - 16 - 32)) & 0x1;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static int turris_omnia_mcu_set_value(struct udevice *dev, uint offset, int value)
+{
+	struct turris_omnia_mcu_info *info = dev_get_plat(dev);
+	u8 val[2];
+	int ret;
+	u8 reg;
+
+	switch (offset) {
+	/* bank 0 */
+	case ilog2(STS_USB30_PWRON):
+		reg = CMD_GENERAL_CONTROL;
+		val[1] = CTL_USB30_PWRON;
+		break;
+	case ilog2(STS_USB31_PWRON):
+		reg = CMD_GENERAL_CONTROL;
+		val[1] = CTL_USB31_PWRON;
+		break;
+	case ilog2(STS_ENABLE_4V5):
+		reg = CMD_GENERAL_CONTROL;
+		val[1] = CTL_ENABLE_4V5;
+		break;
+	case ilog2(STS_BUTTON_MODE):
+		reg = CMD_GENERAL_CONTROL;
+		val[1] = CTL_BUTTON_MODE;
+		break;
+
+	/* bank 2 - supported only when FEAT_EXT_CMDS is set */
+	case (16 + 32 + 0) ... (16 + 32 + 15):
+		if (!(info->features & FEAT_EXT_CMDS))
+			return -EINVAL;
+		reg = CMD_EXT_CONTROL;
+		val[1] = BIT(offset - 16 - 32);
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	val[0] = value ? val[1] : 0;
+
+	ret = dm_i2c_write(dev, reg, val, 2);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int turris_omnia_mcu_direction_input(struct udevice *dev, uint offset)
+{
+	int ret;
+
+	ret = turris_omnia_mcu_get_function(dev, offset);
+	if (ret < 0)
+		return ret;
+	else if (ret != GPIOF_INPUT)
+		return -EOPNOTSUPP;
+
+	return 0;
+}
+
+static int turris_omnia_mcu_direction_output(struct udevice *dev, uint offset, int value)
+{
+	int ret;
+
+	ret = turris_omnia_mcu_get_function(dev, offset);
+	if (ret < 0)
+		return ret;
+	else if (ret != GPIOF_OUTPUT)
+		return -EOPNOTSUPP;
+
+	return turris_omnia_mcu_set_value(dev, offset, value);
+}
+
+static int turris_omnia_mcu_xlate(struct udevice *dev, struct gpio_desc *desc,
+				  struct ofnode_phandle_args *args)
+{
+	uint bank, gpio, flags, offset;
+	int ret;
+
+	if (args->args_count != 3)
+		return -EINVAL;
+
+	bank = args->args[0];
+	gpio = args->args[1];
+	flags = args->args[2];
+
+	switch (bank) {
+	case 0:
+		if (gpio >= 16)
+			return -EINVAL;
+		offset = gpio;
+		break;
+	case 1:
+		if (gpio >= 32)
+			return -EINVAL;
+		offset = 16 + gpio;
+		break;
+	case 2:
+		if (gpio >= 16)
+			return -EINVAL;
+		offset = 16 + 32 + gpio;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = turris_omnia_mcu_get_function(dev, offset);
+	if (ret < 0)
+		return ret;
+
+	if (ret == GPIOF_OUTPUT && ((flags & GPIOD_IS_IN) || !(flags & GPIOD_IS_OUT)))
+		return -EINVAL;
+	else if (ret == GPIOF_INPUT && (!(flags & GPIOD_IS_IN) || (flags & GPIOD_IS_OUT)))
+		return -EINVAL;
+
+	desc->offset = offset;
+	desc->flags = flags;
+
+	return 0;
+}
+
+static const struct dm_gpio_ops turris_omnia_mcu_ops = {
+	.direction_input	= turris_omnia_mcu_direction_input,
+	.direction_output	= turris_omnia_mcu_direction_output,
+	.get_value		= turris_omnia_mcu_get_value,
+	.set_value		= turris_omnia_mcu_set_value,
+	.get_function		= turris_omnia_mcu_get_function,
+	.xlate			= turris_omnia_mcu_xlate,
+};
+
+static int turris_omnia_mcu_probe(struct udevice *dev)
+{
+	struct turris_omnia_mcu_info *info = dev_get_plat(dev);
+	struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+	u16 status;
+	u8 val[2];
+	int ret;
+
+	ret = dm_i2c_read(dev, CMD_GET_STATUS_WORD, val, 2);
+	if (ret) {
+		printf("Error: turris_omnia_mcu CMD_GET_STATUS_WORD failed: %d\n", ret);
+		return ret;
+	}
+
+	status = ((u16)val[1] << 8) | val[0];
+
+	if (status & STS_FEATURES_SUPPORTED) {
+		ret = dm_i2c_read(dev, CMD_GET_FEATURES, val, 2);
+		if (ret) {
+			printf("Error: turris_omnia_mcu CMD_GET_FEATURES failed: %d\n", ret);
+			return ret;
+		}
+		info->features = ((u16)val[1] << 8) | val[0];
+	}
+
+	uc_priv->bank_name = "mcu_";
+
+	if (info->features & FEAT_EXT_CMDS)
+		uc_priv->gpio_count = 16 + 32 + 16;
+	else
+		uc_priv->gpio_count = 16;
+
+	return 0;
+}
+
+static const struct udevice_id turris_omnia_mcu_ids[] = {
+	{ .compatible = "cznic,turris-omnia-mcu" },
+	{ }
+};
+
+U_BOOT_DRIVER(turris_omnia_mcu) = {
+	.name		= "turris-omnia-mcu",
+	.id		= UCLASS_GPIO,
+	.ops		= &turris_omnia_mcu_ops,
+	.probe		= turris_omnia_mcu_probe,
+	.plat_auto	= sizeof(struct turris_omnia_mcu_info),
+	.of_match	= turris_omnia_mcu_ids,
+};
-- 
2.20.1


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

* [PATCH 2/2] arm: mvebu: turris_omnia: Add mcu node with gpio-controller
  2022-07-25 13:01 [PATCH 1/2] gpio: Add Turris Omnia MCU driver Pali Rohár
@ 2022-07-25 13:01 ` Pali Rohár
  2022-07-28  6:34   ` Stefan Roese
  2022-07-28  6:33 ` [PATCH 1/2] gpio: Add Turris Omnia MCU driver Stefan Roese
  2022-07-28 11:06 ` [PATCH v2 " Pali Rohár
  2 siblings, 1 reply; 10+ messages in thread
From: Pali Rohár @ 2022-07-25 13:01 UTC (permalink / raw)
  To: Stefan Roese; +Cc: Marek Behun, u-boot

This allows U-Boot to register new Turris Omnia MCU driver.

Signed-off-by: Pali Rohár <pali@kernel.org>
---
 arch/arm/dts/armada-385-turris-omnia.dts | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/arch/arm/dts/armada-385-turris-omnia.dts b/arch/arm/dts/armada-385-turris-omnia.dts
index 7f1478edfd23..fe5c0b1b5915 100644
--- a/arch/arm/dts/armada-385-turris-omnia.dts
+++ b/arch/arm/dts/armada-385-turris-omnia.dts
@@ -173,7 +173,13 @@
 			#size-cells = <0>;
 			reg = <0>;
 
-			/* STM32F0 command interface at address 0x2a */
+			/* MCU command i2c API */
+			mcu: mcu@2a {
+				compatible = "cznic,turris-omnia-mcu";
+				reg = <0x2a>;
+				gpio-controller;
+			};
+
 			/* leds device (in STM32F0) at address 0x2b */
 
 			eeprom@54 {
-- 
2.20.1


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

* Re: [PATCH 1/2] gpio: Add Turris Omnia MCU driver
  2022-07-25 13:01 [PATCH 1/2] gpio: Add Turris Omnia MCU driver Pali Rohár
  2022-07-25 13:01 ` [PATCH 2/2] arm: mvebu: turris_omnia: Add mcu node with gpio-controller Pali Rohár
@ 2022-07-28  6:33 ` Stefan Roese
  2022-07-28 11:06 ` [PATCH v2 " Pali Rohár
  2 siblings, 0 replies; 10+ messages in thread
From: Stefan Roese @ 2022-07-28  6:33 UTC (permalink / raw)
  To: Pali Rohár; +Cc: Marek Behun, u-boot

On 25.07.22 15:01, Pali Rohár wrote:
> This driver registers GPIO controller and allows U-Boot to control GPIO
> pins on MCU which is connected to Turris Omnia via i2c.
> 
> Signed-off-by: Pali Rohár <pali@kernel.org>

Reviewed-by: Stefan Roese <sr@denx.de>

Thanks,
Stefan

> ---
>   board/CZ.NIC/turris_omnia/MAINTAINERS |   1 +
>   drivers/gpio/Kconfig                  |   7 +
>   drivers/gpio/Makefile                 |   1 +
>   drivers/gpio/turris_omnia_mcu.c       | 314 ++++++++++++++++++++++++++
>   4 files changed, 323 insertions(+)
>   create mode 100644 drivers/gpio/turris_omnia_mcu.c
> 
> diff --git a/board/CZ.NIC/turris_omnia/MAINTAINERS b/board/CZ.NIC/turris_omnia/MAINTAINERS
> index 8258f4fc5b23..8bff97cc2737 100644
> --- a/board/CZ.NIC/turris_omnia/MAINTAINERS
> +++ b/board/CZ.NIC/turris_omnia/MAINTAINERS
> @@ -6,4 +6,5 @@ F:	arch/arm/dts/armada-385-turris-omnia*.dts*
>   F:	board/CZ.NIC/turris_atsha_otp.*
>   F:	board/CZ.NIC/turris_omnia/
>   F:	configs/turris_omnia_defconfig
> +F:	drivers/gpio/turris_omnia_mcu.c
>   F:	include/configs/turris_omnia.h
> diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
> index aaa152fae73b..82a8bca27000 100644
> --- a/drivers/gpio/Kconfig
> +++ b/drivers/gpio/Kconfig
> @@ -598,4 +598,11 @@ config SLG7XL45106_I2C_GPO
>   	   8-bit gpo expander, all gpo lines are controlled by writing
>   	   value into data register.
>   
> +config TURRIS_OMNIA_MCU
> +	bool "Turris Omnia MCU GPIO driver"
> +	depends on DM_GPIO
> +	default y if TARGET_TURRIS_OMNIA
> +	help
> +	   Support for GPIOs on MCU connected to Turris Omnia via i2c.
> +
>   endif
> diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
> index d7552762d065..219f37e0e434 100644
> --- a/drivers/gpio/Makefile
> +++ b/drivers/gpio/Makefile
> @@ -75,3 +75,4 @@ obj-$(CONFIG_MAX7320_GPIO)	+= max7320_gpio.o
>   obj-$(CONFIG_SL28CPLD_GPIO)	+= sl28cpld-gpio.o
>   obj-$(CONFIG_ZYNQMP_GPIO_MODEPIN)	+= zynqmp_gpio_modepin.o
>   obj-$(CONFIG_SLG7XL45106_I2C_GPO)	+= gpio_slg7xl45106.o
> +obj-$(CONFIG_$(SPL_TPL_)TURRIS_OMNIA_MCU)	+= turris_omnia_mcu.o
> diff --git a/drivers/gpio/turris_omnia_mcu.c b/drivers/gpio/turris_omnia_mcu.c
> new file mode 100644
> index 000000000000..44a6574d5f17
> --- /dev/null
> +++ b/drivers/gpio/turris_omnia_mcu.c
> @@ -0,0 +1,314 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +// (C) 2022 Pali Rohár <pali@kernel.org>
> +
> +#include <common.h>
> +#include <dm.h>
> +#include <i2c.h>
> +#include <asm/gpio.h>
> +#include <linux/log2.h>
> +
> +enum commands_e {
> +	CMD_GET_STATUS_WORD                 = 0x01,
> +	CMD_GENERAL_CONTROL                 = 0x02,
> +
> +	/* available if STS_FEATURES_SUPPORTED bit set in status word */
> +	CMD_GET_FEATURES                    = 0x10,
> +
> +	/* available if FEAT_EXT_CMDS bit is set in features */
> +	CMD_GET_EXT_STATUS_DWORD            = 0x11,
> +	CMD_EXT_CONTROL                     = 0x12,
> +	CMD_GET_EXT_CONTROL_STATUS          = 0x13,
> +};
> +
> +/* CMD_GET_STATUS_WORD */
> +enum sts_word_e {
> +	STS_MCU_TYPE_MASK                = GENMASK(1, 0),
> +	STS_MCU_TYPE_STM32               = 0,
> +	STS_MCU_TYPE_GD32                = 1,
> +	STS_MCU_TYPE_MKL                 = 2,
> +	STS_FEATURES_SUPPORTED           = BIT(2),
> +	STS_USER_REGULATOR_NOT_SUPPORTED = BIT(3),
> +	STS_CARD_DET                     = BIT(4),
> +	STS_MSATA_IND                    = BIT(5),
> +	STS_USB30_OVC                    = BIT(6),
> +	STS_USB31_OVC                    = BIT(7),
> +	STS_USB30_PWRON                  = BIT(8),
> +	STS_USB31_PWRON                  = BIT(9),
> +	STS_ENABLE_4V5                   = BIT(10),
> +	STS_BUTTON_MODE                  = BIT(11),
> +	STS_BUTTON_PRESSED               = BIT(12),
> +	STS_BUTTON_COUNTER_MASK          = GENMASK(15, 13)
> +};
> +
> +/* CMD_GENERAL_CONTROL */
> +enum ctl_byte_e {
> +	CTL_LIGHT_RST   = BIT(0),
> +	CTL_HARD_RST    = BIT(1),
> +	/*CTL_RESERVED    = BIT(2),*/
> +	CTL_USB30_PWRON = BIT(3),
> +	CTL_USB31_PWRON = BIT(4),
> +	CTL_ENABLE_4V5  = BIT(5),
> +	CTL_BUTTON_MODE = BIT(6),
> +	CTL_BOOTLOADER  = BIT(7)
> +};
> +
> +/* CMD_GET_FEATURES */
> +enum features_e {
> +	FEAT_EXT_CMDS           = BIT(1),
> +};
> +
> +struct turris_omnia_mcu_info {
> +	u16 features;
> +};
> +
> +static int turris_omnia_mcu_get_function(struct udevice *dev, uint offset)
> +{
> +	struct turris_omnia_mcu_info *info = dev_get_plat(dev);
> +
> +	switch (offset) {
> +	/* bank 0 */
> +	case 0 ... 15:
> +		switch (offset) {
> +		case ilog2(STS_USB30_PWRON):
> +		case ilog2(STS_USB31_PWRON):
> +		case ilog2(STS_ENABLE_4V5):
> +		case ilog2(STS_BUTTON_MODE):
> +			return GPIOF_OUTPUT;
> +		default:
> +			return GPIOF_INPUT;
> +		}
> +
> +	/* bank 1 - supported only when FEAT_EXT_CMDS is set */
> +	case (16 + 0) ... (16 + 31):
> +		if (!(info->features & FEAT_EXT_CMDS))
> +			return -EINVAL;
> +		return GPIOF_INPUT;
> +
> +	/* bank 2 - supported only when FEAT_EXT_CMDS is set */
> +	case (16 + 32 + 0) ... (16 + 32 + 15):
> +		if (!(info->features & FEAT_EXT_CMDS))
> +			return -EINVAL;
> +		return GPIOF_OUTPUT;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static int turris_omnia_mcu_get_value(struct udevice *dev, uint offset)
> +{
> +	struct turris_omnia_mcu_info *info = dev_get_plat(dev);
> +	u8 val16[2];
> +	u8 val32[4];
> +	int ret;
> +
> +	switch (offset) {
> +	/* bank 0 */
> +	case 0 ... 15:
> +		ret = dm_i2c_read(dev, CMD_GET_STATUS_WORD, val16, 2);
> +		if (ret)
> +			return ret;
> +		return ((((u16)val16[1] << 8) | val16[0]) >> offset) & 0x1;
> +
> +	/* bank 1 - supported only when FEAT_EXT_CMDS is set */
> +	case (16 + 0) ... (16 + 31):
> +		if (!(info->features & FEAT_EXT_CMDS))
> +			return -EINVAL;
> +		ret = dm_i2c_read(dev, CMD_GET_EXT_STATUS_DWORD, val32, 4);
> +		if (ret)
> +			return ret;
> +		return ((((u32)val32[3] << 24) | ((u32)val32[2] << 16) |
> +			 ((u32)val32[1] << 8) | val32[0]) >> (offset - 16)) & 0x1;
> +
> +	/* bank 2 - supported only when FEAT_EXT_CMDS is set */
> +	case (16 + 32 + 0) ... (16 + 32 + 15):
> +		if (!(info->features & FEAT_EXT_CMDS))
> +			return -EINVAL;
> +		ret = dm_i2c_read(dev, CMD_GET_EXT_CONTROL_STATUS, val16, 2);
> +		if (ret)
> +			return ret;
> +		return ((((u16)val16[1] << 8) | val16[0]) >> (offset - 16 - 32)) & 0x1;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static int turris_omnia_mcu_set_value(struct udevice *dev, uint offset, int value)
> +{
> +	struct turris_omnia_mcu_info *info = dev_get_plat(dev);
> +	u8 val[2];
> +	int ret;
> +	u8 reg;
> +
> +	switch (offset) {
> +	/* bank 0 */
> +	case ilog2(STS_USB30_PWRON):
> +		reg = CMD_GENERAL_CONTROL;
> +		val[1] = CTL_USB30_PWRON;
> +		break;
> +	case ilog2(STS_USB31_PWRON):
> +		reg = CMD_GENERAL_CONTROL;
> +		val[1] = CTL_USB31_PWRON;
> +		break;
> +	case ilog2(STS_ENABLE_4V5):
> +		reg = CMD_GENERAL_CONTROL;
> +		val[1] = CTL_ENABLE_4V5;
> +		break;
> +	case ilog2(STS_BUTTON_MODE):
> +		reg = CMD_GENERAL_CONTROL;
> +		val[1] = CTL_BUTTON_MODE;
> +		break;
> +
> +	/* bank 2 - supported only when FEAT_EXT_CMDS is set */
> +	case (16 + 32 + 0) ... (16 + 32 + 15):
> +		if (!(info->features & FEAT_EXT_CMDS))
> +			return -EINVAL;
> +		reg = CMD_EXT_CONTROL;
> +		val[1] = BIT(offset - 16 - 32);
> +		break;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	val[0] = value ? val[1] : 0;
> +
> +	ret = dm_i2c_write(dev, reg, val, 2);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static int turris_omnia_mcu_direction_input(struct udevice *dev, uint offset)
> +{
> +	int ret;
> +
> +	ret = turris_omnia_mcu_get_function(dev, offset);
> +	if (ret < 0)
> +		return ret;
> +	else if (ret != GPIOF_INPUT)
> +		return -EOPNOTSUPP;
> +
> +	return 0;
> +}
> +
> +static int turris_omnia_mcu_direction_output(struct udevice *dev, uint offset, int value)
> +{
> +	int ret;
> +
> +	ret = turris_omnia_mcu_get_function(dev, offset);
> +	if (ret < 0)
> +		return ret;
> +	else if (ret != GPIOF_OUTPUT)
> +		return -EOPNOTSUPP;
> +
> +	return turris_omnia_mcu_set_value(dev, offset, value);
> +}
> +
> +static int turris_omnia_mcu_xlate(struct udevice *dev, struct gpio_desc *desc,
> +				  struct ofnode_phandle_args *args)
> +{
> +	uint bank, gpio, flags, offset;
> +	int ret;
> +
> +	if (args->args_count != 3)
> +		return -EINVAL;
> +
> +	bank = args->args[0];
> +	gpio = args->args[1];
> +	flags = args->args[2];
> +
> +	switch (bank) {
> +	case 0:
> +		if (gpio >= 16)
> +			return -EINVAL;
> +		offset = gpio;
> +		break;
> +	case 1:
> +		if (gpio >= 32)
> +			return -EINVAL;
> +		offset = 16 + gpio;
> +		break;
> +	case 2:
> +		if (gpio >= 16)
> +			return -EINVAL;
> +		offset = 16 + 32 + gpio;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	ret = turris_omnia_mcu_get_function(dev, offset);
> +	if (ret < 0)
> +		return ret;
> +
> +	if (ret == GPIOF_OUTPUT && ((flags & GPIOD_IS_IN) || !(flags & GPIOD_IS_OUT)))
> +		return -EINVAL;
> +	else if (ret == GPIOF_INPUT && (!(flags & GPIOD_IS_IN) || (flags & GPIOD_IS_OUT)))
> +		return -EINVAL;
> +
> +	desc->offset = offset;
> +	desc->flags = flags;
> +
> +	return 0;
> +}
> +
> +static const struct dm_gpio_ops turris_omnia_mcu_ops = {
> +	.direction_input	= turris_omnia_mcu_direction_input,
> +	.direction_output	= turris_omnia_mcu_direction_output,
> +	.get_value		= turris_omnia_mcu_get_value,
> +	.set_value		= turris_omnia_mcu_set_value,
> +	.get_function		= turris_omnia_mcu_get_function,
> +	.xlate			= turris_omnia_mcu_xlate,
> +};
> +
> +static int turris_omnia_mcu_probe(struct udevice *dev)
> +{
> +	struct turris_omnia_mcu_info *info = dev_get_plat(dev);
> +	struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
> +	u16 status;
> +	u8 val[2];
> +	int ret;
> +
> +	ret = dm_i2c_read(dev, CMD_GET_STATUS_WORD, val, 2);
> +	if (ret) {
> +		printf("Error: turris_omnia_mcu CMD_GET_STATUS_WORD failed: %d\n", ret);
> +		return ret;
> +	}
> +
> +	status = ((u16)val[1] << 8) | val[0];
> +
> +	if (status & STS_FEATURES_SUPPORTED) {
> +		ret = dm_i2c_read(dev, CMD_GET_FEATURES, val, 2);
> +		if (ret) {
> +			printf("Error: turris_omnia_mcu CMD_GET_FEATURES failed: %d\n", ret);
> +			return ret;
> +		}
> +		info->features = ((u16)val[1] << 8) | val[0];
> +	}
> +
> +	uc_priv->bank_name = "mcu_";
> +
> +	if (info->features & FEAT_EXT_CMDS)
> +		uc_priv->gpio_count = 16 + 32 + 16;
> +	else
> +		uc_priv->gpio_count = 16;
> +
> +	return 0;
> +}
> +
> +static const struct udevice_id turris_omnia_mcu_ids[] = {
> +	{ .compatible = "cznic,turris-omnia-mcu" },
> +	{ }
> +};
> +
> +U_BOOT_DRIVER(turris_omnia_mcu) = {
> +	.name		= "turris-omnia-mcu",
> +	.id		= UCLASS_GPIO,
> +	.ops		= &turris_omnia_mcu_ops,
> +	.probe		= turris_omnia_mcu_probe,
> +	.plat_auto	= sizeof(struct turris_omnia_mcu_info),
> +	.of_match	= turris_omnia_mcu_ids,
> +};

Viele Grüße,
Stefan Roese

-- 
DENX Software Engineering GmbH,      Managing Director: Wolfgang Denk
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-51 Fax: (+49)-8142-66989-80 Email: sr@denx.de

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

* Re: [PATCH 2/2] arm: mvebu: turris_omnia: Add mcu node with gpio-controller
  2022-07-25 13:01 ` [PATCH 2/2] arm: mvebu: turris_omnia: Add mcu node with gpio-controller Pali Rohár
@ 2022-07-28  6:34   ` Stefan Roese
  0 siblings, 0 replies; 10+ messages in thread
From: Stefan Roese @ 2022-07-28  6:34 UTC (permalink / raw)
  To: Pali Rohár; +Cc: Marek Behun, u-boot

On 25.07.22 15:01, Pali Rohár wrote:
> This allows U-Boot to register new Turris Omnia MCU driver.
> 
> Signed-off-by: Pali Rohár <pali@kernel.org>

Reviewed-by: Stefan Roese <sr@denx.de>

Thanks,
Stefan

> ---
>   arch/arm/dts/armada-385-turris-omnia.dts | 8 +++++++-
>   1 file changed, 7 insertions(+), 1 deletion(-)
> 
> diff --git a/arch/arm/dts/armada-385-turris-omnia.dts b/arch/arm/dts/armada-385-turris-omnia.dts
> index 7f1478edfd23..fe5c0b1b5915 100644
> --- a/arch/arm/dts/armada-385-turris-omnia.dts
> +++ b/arch/arm/dts/armada-385-turris-omnia.dts
> @@ -173,7 +173,13 @@
>   			#size-cells = <0>;
>   			reg = <0>;
>   
> -			/* STM32F0 command interface at address 0x2a */
> +			/* MCU command i2c API */
> +			mcu: mcu@2a {
> +				compatible = "cznic,turris-omnia-mcu";
> +				reg = <0x2a>;
> +				gpio-controller;
> +			};
> +
>   			/* leds device (in STM32F0) at address 0x2b */
>   
>   			eeprom@54 {

Viele Grüße,
Stefan Roese

-- 
DENX Software Engineering GmbH,      Managing Director: Wolfgang Denk
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-51 Fax: (+49)-8142-66989-80 Email: sr@denx.de

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

* [PATCH v2 1/2] gpio: Add Turris Omnia MCU driver
  2022-07-25 13:01 [PATCH 1/2] gpio: Add Turris Omnia MCU driver Pali Rohár
  2022-07-25 13:01 ` [PATCH 2/2] arm: mvebu: turris_omnia: Add mcu node with gpio-controller Pali Rohár
  2022-07-28  6:33 ` [PATCH 1/2] gpio: Add Turris Omnia MCU driver Stefan Roese
@ 2022-07-28 11:06 ` Pali Rohár
  2022-07-28 11:06   ` [PATCH v2 2/2] arm: mvebu: turris_omnia: Add mcu node with gpio-controller Pali Rohár
                     ` (2 more replies)
  2 siblings, 3 replies; 10+ messages in thread
From: Pali Rohár @ 2022-07-28 11:06 UTC (permalink / raw)
  To: Stefan Roese; +Cc: Marek Behun, u-boot

This driver registers GPIO controller and allows U-Boot to control GPIO
pins on MCU which is connected to Turris Omnia via i2c.

Signed-off-by: Pali Rohár <pali@kernel.org>

---
Changes in v2:
* Remove invalid checks in xlate (GPIOF_* vs GPIO_* macros)
* Fix parsing flags in xlate
* Use gpio_flags_xlate(flags) for desc->flags in xlate
---
 board/CZ.NIC/turris_omnia/MAINTAINERS |   1 +
 drivers/gpio/Kconfig                  |   7 +
 drivers/gpio/Makefile                 |   1 +
 drivers/gpio/turris_omnia_mcu.c       | 309 ++++++++++++++++++++++++++
 4 files changed, 318 insertions(+)
 create mode 100644 drivers/gpio/turris_omnia_mcu.c

diff --git a/board/CZ.NIC/turris_omnia/MAINTAINERS b/board/CZ.NIC/turris_omnia/MAINTAINERS
index 8258f4fc5b23..8bff97cc2737 100644
--- a/board/CZ.NIC/turris_omnia/MAINTAINERS
+++ b/board/CZ.NIC/turris_omnia/MAINTAINERS
@@ -6,4 +6,5 @@ F:	arch/arm/dts/armada-385-turris-omnia*.dts*
 F:	board/CZ.NIC/turris_atsha_otp.*
 F:	board/CZ.NIC/turris_omnia/
 F:	configs/turris_omnia_defconfig
+F:	drivers/gpio/turris_omnia_mcu.c
 F:	include/configs/turris_omnia.h
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index aaa152fae73b..82a8bca27000 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -598,4 +598,11 @@ config SLG7XL45106_I2C_GPO
 	   8-bit gpo expander, all gpo lines are controlled by writing
 	   value into data register.
 
+config TURRIS_OMNIA_MCU
+	bool "Turris Omnia MCU GPIO driver"
+	depends on DM_GPIO
+	default y if TARGET_TURRIS_OMNIA
+	help
+	   Support for GPIOs on MCU connected to Turris Omnia via i2c.
+
 endif
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index d7552762d065..219f37e0e434 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -75,3 +75,4 @@ obj-$(CONFIG_MAX7320_GPIO)	+= max7320_gpio.o
 obj-$(CONFIG_SL28CPLD_GPIO)	+= sl28cpld-gpio.o
 obj-$(CONFIG_ZYNQMP_GPIO_MODEPIN)	+= zynqmp_gpio_modepin.o
 obj-$(CONFIG_SLG7XL45106_I2C_GPO)	+= gpio_slg7xl45106.o
+obj-$(CONFIG_$(SPL_TPL_)TURRIS_OMNIA_MCU)	+= turris_omnia_mcu.o
diff --git a/drivers/gpio/turris_omnia_mcu.c b/drivers/gpio/turris_omnia_mcu.c
new file mode 100644
index 000000000000..3e5d74e62cdb
--- /dev/null
+++ b/drivers/gpio/turris_omnia_mcu.c
@@ -0,0 +1,309 @@
+// SPDX-License-Identifier: GPL-2.0+
+// (C) 2022 Pali Rohár <pali@kernel.org>
+
+#include <common.h>
+#include <dm.h>
+#include <i2c.h>
+#include <asm/gpio.h>
+#include <linux/log2.h>
+
+enum commands_e {
+	CMD_GET_STATUS_WORD                 = 0x01,
+	CMD_GENERAL_CONTROL                 = 0x02,
+
+	/* available if STS_FEATURES_SUPPORTED bit set in status word */
+	CMD_GET_FEATURES                    = 0x10,
+
+	/* available if FEAT_EXT_CMDS bit is set in features */
+	CMD_GET_EXT_STATUS_DWORD            = 0x11,
+	CMD_EXT_CONTROL                     = 0x12,
+	CMD_GET_EXT_CONTROL_STATUS          = 0x13,
+};
+
+/* CMD_GET_STATUS_WORD */
+enum sts_word_e {
+	STS_MCU_TYPE_MASK                = GENMASK(1, 0),
+	STS_MCU_TYPE_STM32               = 0,
+	STS_MCU_TYPE_GD32                = 1,
+	STS_MCU_TYPE_MKL                 = 2,
+	STS_FEATURES_SUPPORTED           = BIT(2),
+	STS_USER_REGULATOR_NOT_SUPPORTED = BIT(3),
+	STS_CARD_DET                     = BIT(4),
+	STS_MSATA_IND                    = BIT(5),
+	STS_USB30_OVC                    = BIT(6),
+	STS_USB31_OVC                    = BIT(7),
+	STS_USB30_PWRON                  = BIT(8),
+	STS_USB31_PWRON                  = BIT(9),
+	STS_ENABLE_4V5                   = BIT(10),
+	STS_BUTTON_MODE                  = BIT(11),
+	STS_BUTTON_PRESSED               = BIT(12),
+	STS_BUTTON_COUNTER_MASK          = GENMASK(15, 13)
+};
+
+/* CMD_GENERAL_CONTROL */
+enum ctl_byte_e {
+	CTL_LIGHT_RST   = BIT(0),
+	CTL_HARD_RST    = BIT(1),
+	/*CTL_RESERVED    = BIT(2),*/
+	CTL_USB30_PWRON = BIT(3),
+	CTL_USB31_PWRON = BIT(4),
+	CTL_ENABLE_4V5  = BIT(5),
+	CTL_BUTTON_MODE = BIT(6),
+	CTL_BOOTLOADER  = BIT(7)
+};
+
+/* CMD_GET_FEATURES */
+enum features_e {
+	FEAT_EXT_CMDS           = BIT(1),
+};
+
+struct turris_omnia_mcu_info {
+	u16 features;
+};
+
+static int turris_omnia_mcu_get_function(struct udevice *dev, uint offset)
+{
+	struct turris_omnia_mcu_info *info = dev_get_plat(dev);
+
+	switch (offset) {
+	/* bank 0 */
+	case 0 ... 15:
+		switch (offset) {
+		case ilog2(STS_USB30_PWRON):
+		case ilog2(STS_USB31_PWRON):
+		case ilog2(STS_ENABLE_4V5):
+		case ilog2(STS_BUTTON_MODE):
+			return GPIOF_OUTPUT;
+		default:
+			return GPIOF_INPUT;
+		}
+
+	/* bank 1 - supported only when FEAT_EXT_CMDS is set */
+	case (16 + 0) ... (16 + 31):
+		if (!(info->features & FEAT_EXT_CMDS))
+			return -EINVAL;
+		return GPIOF_INPUT;
+
+	/* bank 2 - supported only when FEAT_EXT_CMDS is set */
+	case (16 + 32 + 0) ... (16 + 32 + 15):
+		if (!(info->features & FEAT_EXT_CMDS))
+			return -EINVAL;
+		return GPIOF_OUTPUT;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static int turris_omnia_mcu_get_value(struct udevice *dev, uint offset)
+{
+	struct turris_omnia_mcu_info *info = dev_get_plat(dev);
+	u8 val16[2];
+	u8 val32[4];
+	int ret;
+
+	switch (offset) {
+	/* bank 0 */
+	case 0 ... 15:
+		ret = dm_i2c_read(dev, CMD_GET_STATUS_WORD, val16, 2);
+		if (ret)
+			return ret;
+		return ((((u16)val16[1] << 8) | val16[0]) >> offset) & 0x1;
+
+	/* bank 1 - supported only when FEAT_EXT_CMDS is set */
+	case (16 + 0) ... (16 + 31):
+		if (!(info->features & FEAT_EXT_CMDS))
+			return -EINVAL;
+		ret = dm_i2c_read(dev, CMD_GET_EXT_STATUS_DWORD, val32, 4);
+		if (ret)
+			return ret;
+		return ((((u32)val32[3] << 24) | ((u32)val32[2] << 16) |
+			 ((u32)val32[1] << 8) | val32[0]) >> (offset - 16)) & 0x1;
+
+	/* bank 2 - supported only when FEAT_EXT_CMDS is set */
+	case (16 + 32 + 0) ... (16 + 32 + 15):
+		if (!(info->features & FEAT_EXT_CMDS))
+			return -EINVAL;
+		ret = dm_i2c_read(dev, CMD_GET_EXT_CONTROL_STATUS, val16, 2);
+		if (ret)
+			return ret;
+		return ((((u16)val16[1] << 8) | val16[0]) >> (offset - 16 - 32)) & 0x1;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static int turris_omnia_mcu_set_value(struct udevice *dev, uint offset, int value)
+{
+	struct turris_omnia_mcu_info *info = dev_get_plat(dev);
+	u8 val[2];
+	int ret;
+	u8 reg;
+
+	switch (offset) {
+	/* bank 0 */
+	case ilog2(STS_USB30_PWRON):
+		reg = CMD_GENERAL_CONTROL;
+		val[1] = CTL_USB30_PWRON;
+		break;
+	case ilog2(STS_USB31_PWRON):
+		reg = CMD_GENERAL_CONTROL;
+		val[1] = CTL_USB31_PWRON;
+		break;
+	case ilog2(STS_ENABLE_4V5):
+		reg = CMD_GENERAL_CONTROL;
+		val[1] = CTL_ENABLE_4V5;
+		break;
+	case ilog2(STS_BUTTON_MODE):
+		reg = CMD_GENERAL_CONTROL;
+		val[1] = CTL_BUTTON_MODE;
+		break;
+
+	/* bank 2 - supported only when FEAT_EXT_CMDS is set */
+	case (16 + 32 + 0) ... (16 + 32 + 15):
+		if (!(info->features & FEAT_EXT_CMDS))
+			return -EINVAL;
+		reg = CMD_EXT_CONTROL;
+		val[1] = BIT(offset - 16 - 32);
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	val[0] = value ? val[1] : 0;
+
+	ret = dm_i2c_write(dev, reg, val, 2);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int turris_omnia_mcu_direction_input(struct udevice *dev, uint offset)
+{
+	int ret;
+
+	ret = turris_omnia_mcu_get_function(dev, offset);
+	if (ret < 0)
+		return ret;
+	else if (ret != GPIOF_INPUT)
+		return -EOPNOTSUPP;
+
+	return 0;
+}
+
+static int turris_omnia_mcu_direction_output(struct udevice *dev, uint offset, int value)
+{
+	int ret;
+
+	ret = turris_omnia_mcu_get_function(dev, offset);
+	if (ret < 0)
+		return ret;
+	else if (ret != GPIOF_OUTPUT)
+		return -EOPNOTSUPP;
+
+	return turris_omnia_mcu_set_value(dev, offset, value);
+}
+
+static int turris_omnia_mcu_xlate(struct udevice *dev, struct gpio_desc *desc,
+				  struct ofnode_phandle_args *args)
+{
+	uint bank, gpio, flags, offset;
+	int ret;
+
+	if (args->args_count != 3)
+		return -EINVAL;
+
+	bank = args->args[0];
+	gpio = args->args[1];
+	flags = args->args[2];
+
+	switch (bank) {
+	case 0:
+		if (gpio >= 16)
+			return -EINVAL;
+		offset = gpio;
+		break;
+	case 1:
+		if (gpio >= 32)
+			return -EINVAL;
+		offset = 16 + gpio;
+		break;
+	case 2:
+		if (gpio >= 16)
+			return -EINVAL;
+		offset = 16 + 32 + gpio;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = turris_omnia_mcu_get_function(dev, offset);
+	if (ret < 0)
+		return ret;
+
+	desc->offset = offset;
+	desc->flags = gpio_flags_xlate(flags);
+
+	return 0;
+}
+
+static const struct dm_gpio_ops turris_omnia_mcu_ops = {
+	.direction_input	= turris_omnia_mcu_direction_input,
+	.direction_output	= turris_omnia_mcu_direction_output,
+	.get_value		= turris_omnia_mcu_get_value,
+	.set_value		= turris_omnia_mcu_set_value,
+	.get_function		= turris_omnia_mcu_get_function,
+	.xlate			= turris_omnia_mcu_xlate,
+};
+
+static int turris_omnia_mcu_probe(struct udevice *dev)
+{
+	struct turris_omnia_mcu_info *info = dev_get_plat(dev);
+	struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+	u16 status;
+	u8 val[2];
+	int ret;
+
+	ret = dm_i2c_read(dev, CMD_GET_STATUS_WORD, val, 2);
+	if (ret) {
+		printf("Error: turris_omnia_mcu CMD_GET_STATUS_WORD failed: %d\n", ret);
+		return ret;
+	}
+
+	status = ((u16)val[1] << 8) | val[0];
+
+	if (status & STS_FEATURES_SUPPORTED) {
+		ret = dm_i2c_read(dev, CMD_GET_FEATURES, val, 2);
+		if (ret) {
+			printf("Error: turris_omnia_mcu CMD_GET_FEATURES failed: %d\n", ret);
+			return ret;
+		}
+		info->features = ((u16)val[1] << 8) | val[0];
+	}
+
+	uc_priv->bank_name = "mcu_";
+
+	if (info->features & FEAT_EXT_CMDS)
+		uc_priv->gpio_count = 16 + 32 + 16;
+	else
+		uc_priv->gpio_count = 16;
+
+	return 0;
+}
+
+static const struct udevice_id turris_omnia_mcu_ids[] = {
+	{ .compatible = "cznic,turris-omnia-mcu" },
+	{ }
+};
+
+U_BOOT_DRIVER(turris_omnia_mcu) = {
+	.name		= "turris-omnia-mcu",
+	.id		= UCLASS_GPIO,
+	.ops		= &turris_omnia_mcu_ops,
+	.probe		= turris_omnia_mcu_probe,
+	.plat_auto	= sizeof(struct turris_omnia_mcu_info),
+	.of_match	= turris_omnia_mcu_ids,
+};
-- 
2.20.1


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

* [PATCH v2 2/2] arm: mvebu: turris_omnia: Add mcu node with gpio-controller
  2022-07-28 11:06 ` [PATCH v2 " Pali Rohár
@ 2022-07-28 11:06   ` Pali Rohár
  2022-07-29  6:28     ` Stefan Roese
  2022-07-29 12:01     ` Stefan Roese
  2022-07-29  6:28   ` [PATCH v2 1/2] gpio: Add Turris Omnia MCU driver Stefan Roese
  2022-07-29 12:01   ` Stefan Roese
  2 siblings, 2 replies; 10+ messages in thread
From: Pali Rohár @ 2022-07-28 11:06 UTC (permalink / raw)
  To: Stefan Roese; +Cc: Marek Behun, u-boot

This allows U-Boot to register new Turris Omnia MCU driver.

Signed-off-by: Pali Rohár <pali@kernel.org>

---
Changes in v2:
* Add missing #gpio-cells for phandle usage
---
 arch/arm/dts/armada-385-turris-omnia.dts | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/arch/arm/dts/armada-385-turris-omnia.dts b/arch/arm/dts/armada-385-turris-omnia.dts
index 7f1478edfd23..86904de816de 100644
--- a/arch/arm/dts/armada-385-turris-omnia.dts
+++ b/arch/arm/dts/armada-385-turris-omnia.dts
@@ -173,7 +173,14 @@
 			#size-cells = <0>;
 			reg = <0>;
 
-			/* STM32F0 command interface at address 0x2a */
+			/* MCU command i2c API */
+			mcu: mcu@2a {
+				compatible = "cznic,turris-omnia-mcu";
+				reg = <0x2a>;
+				gpio-controller;
+				#gpio-cells = <3>;
+			};
+
 			/* leds device (in STM32F0) at address 0x2b */
 
 			eeprom@54 {
-- 
2.20.1


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

* Re: [PATCH v2 1/2] gpio: Add Turris Omnia MCU driver
  2022-07-28 11:06 ` [PATCH v2 " Pali Rohár
  2022-07-28 11:06   ` [PATCH v2 2/2] arm: mvebu: turris_omnia: Add mcu node with gpio-controller Pali Rohár
@ 2022-07-29  6:28   ` Stefan Roese
  2022-07-29 12:01   ` Stefan Roese
  2 siblings, 0 replies; 10+ messages in thread
From: Stefan Roese @ 2022-07-29  6:28 UTC (permalink / raw)
  To: Pali Rohár; +Cc: Marek Behun, u-boot

On 28.07.22 13:06, Pali Rohár wrote:
> This driver registers GPIO controller and allows U-Boot to control GPIO
> pins on MCU which is connected to Turris Omnia via i2c.
> 
> Signed-off-by: Pali Rohár <pali@kernel.org>

Reviewed-by: Stefan Roese <sr@denx.de>

Thanks,
Stefan

> ---
> Changes in v2:
> * Remove invalid checks in xlate (GPIOF_* vs GPIO_* macros)
> * Fix parsing flags in xlate
> * Use gpio_flags_xlate(flags) for desc->flags in xlate
> ---
>   board/CZ.NIC/turris_omnia/MAINTAINERS |   1 +
>   drivers/gpio/Kconfig                  |   7 +
>   drivers/gpio/Makefile                 |   1 +
>   drivers/gpio/turris_omnia_mcu.c       | 309 ++++++++++++++++++++++++++
>   4 files changed, 318 insertions(+)
>   create mode 100644 drivers/gpio/turris_omnia_mcu.c
> 
> diff --git a/board/CZ.NIC/turris_omnia/MAINTAINERS b/board/CZ.NIC/turris_omnia/MAINTAINERS
> index 8258f4fc5b23..8bff97cc2737 100644
> --- a/board/CZ.NIC/turris_omnia/MAINTAINERS
> +++ b/board/CZ.NIC/turris_omnia/MAINTAINERS
> @@ -6,4 +6,5 @@ F:	arch/arm/dts/armada-385-turris-omnia*.dts*
>   F:	board/CZ.NIC/turris_atsha_otp.*
>   F:	board/CZ.NIC/turris_omnia/
>   F:	configs/turris_omnia_defconfig
> +F:	drivers/gpio/turris_omnia_mcu.c
>   F:	include/configs/turris_omnia.h
> diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
> index aaa152fae73b..82a8bca27000 100644
> --- a/drivers/gpio/Kconfig
> +++ b/drivers/gpio/Kconfig
> @@ -598,4 +598,11 @@ config SLG7XL45106_I2C_GPO
>   	   8-bit gpo expander, all gpo lines are controlled by writing
>   	   value into data register.
>   
> +config TURRIS_OMNIA_MCU
> +	bool "Turris Omnia MCU GPIO driver"
> +	depends on DM_GPIO
> +	default y if TARGET_TURRIS_OMNIA
> +	help
> +	   Support for GPIOs on MCU connected to Turris Omnia via i2c.
> +
>   endif
> diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
> index d7552762d065..219f37e0e434 100644
> --- a/drivers/gpio/Makefile
> +++ b/drivers/gpio/Makefile
> @@ -75,3 +75,4 @@ obj-$(CONFIG_MAX7320_GPIO)	+= max7320_gpio.o
>   obj-$(CONFIG_SL28CPLD_GPIO)	+= sl28cpld-gpio.o
>   obj-$(CONFIG_ZYNQMP_GPIO_MODEPIN)	+= zynqmp_gpio_modepin.o
>   obj-$(CONFIG_SLG7XL45106_I2C_GPO)	+= gpio_slg7xl45106.o
> +obj-$(CONFIG_$(SPL_TPL_)TURRIS_OMNIA_MCU)	+= turris_omnia_mcu.o
> diff --git a/drivers/gpio/turris_omnia_mcu.c b/drivers/gpio/turris_omnia_mcu.c
> new file mode 100644
> index 000000000000..3e5d74e62cdb
> --- /dev/null
> +++ b/drivers/gpio/turris_omnia_mcu.c
> @@ -0,0 +1,309 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +// (C) 2022 Pali Rohár <pali@kernel.org>
> +
> +#include <common.h>
> +#include <dm.h>
> +#include <i2c.h>
> +#include <asm/gpio.h>
> +#include <linux/log2.h>
> +
> +enum commands_e {
> +	CMD_GET_STATUS_WORD                 = 0x01,
> +	CMD_GENERAL_CONTROL                 = 0x02,
> +
> +	/* available if STS_FEATURES_SUPPORTED bit set in status word */
> +	CMD_GET_FEATURES                    = 0x10,
> +
> +	/* available if FEAT_EXT_CMDS bit is set in features */
> +	CMD_GET_EXT_STATUS_DWORD            = 0x11,
> +	CMD_EXT_CONTROL                     = 0x12,
> +	CMD_GET_EXT_CONTROL_STATUS          = 0x13,
> +};
> +
> +/* CMD_GET_STATUS_WORD */
> +enum sts_word_e {
> +	STS_MCU_TYPE_MASK                = GENMASK(1, 0),
> +	STS_MCU_TYPE_STM32               = 0,
> +	STS_MCU_TYPE_GD32                = 1,
> +	STS_MCU_TYPE_MKL                 = 2,
> +	STS_FEATURES_SUPPORTED           = BIT(2),
> +	STS_USER_REGULATOR_NOT_SUPPORTED = BIT(3),
> +	STS_CARD_DET                     = BIT(4),
> +	STS_MSATA_IND                    = BIT(5),
> +	STS_USB30_OVC                    = BIT(6),
> +	STS_USB31_OVC                    = BIT(7),
> +	STS_USB30_PWRON                  = BIT(8),
> +	STS_USB31_PWRON                  = BIT(9),
> +	STS_ENABLE_4V5                   = BIT(10),
> +	STS_BUTTON_MODE                  = BIT(11),
> +	STS_BUTTON_PRESSED               = BIT(12),
> +	STS_BUTTON_COUNTER_MASK          = GENMASK(15, 13)
> +};
> +
> +/* CMD_GENERAL_CONTROL */
> +enum ctl_byte_e {
> +	CTL_LIGHT_RST   = BIT(0),
> +	CTL_HARD_RST    = BIT(1),
> +	/*CTL_RESERVED    = BIT(2),*/
> +	CTL_USB30_PWRON = BIT(3),
> +	CTL_USB31_PWRON = BIT(4),
> +	CTL_ENABLE_4V5  = BIT(5),
> +	CTL_BUTTON_MODE = BIT(6),
> +	CTL_BOOTLOADER  = BIT(7)
> +};
> +
> +/* CMD_GET_FEATURES */
> +enum features_e {
> +	FEAT_EXT_CMDS           = BIT(1),
> +};
> +
> +struct turris_omnia_mcu_info {
> +	u16 features;
> +};
> +
> +static int turris_omnia_mcu_get_function(struct udevice *dev, uint offset)
> +{
> +	struct turris_omnia_mcu_info *info = dev_get_plat(dev);
> +
> +	switch (offset) {
> +	/* bank 0 */
> +	case 0 ... 15:
> +		switch (offset) {
> +		case ilog2(STS_USB30_PWRON):
> +		case ilog2(STS_USB31_PWRON):
> +		case ilog2(STS_ENABLE_4V5):
> +		case ilog2(STS_BUTTON_MODE):
> +			return GPIOF_OUTPUT;
> +		default:
> +			return GPIOF_INPUT;
> +		}
> +
> +	/* bank 1 - supported only when FEAT_EXT_CMDS is set */
> +	case (16 + 0) ... (16 + 31):
> +		if (!(info->features & FEAT_EXT_CMDS))
> +			return -EINVAL;
> +		return GPIOF_INPUT;
> +
> +	/* bank 2 - supported only when FEAT_EXT_CMDS is set */
> +	case (16 + 32 + 0) ... (16 + 32 + 15):
> +		if (!(info->features & FEAT_EXT_CMDS))
> +			return -EINVAL;
> +		return GPIOF_OUTPUT;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static int turris_omnia_mcu_get_value(struct udevice *dev, uint offset)
> +{
> +	struct turris_omnia_mcu_info *info = dev_get_plat(dev);
> +	u8 val16[2];
> +	u8 val32[4];
> +	int ret;
> +
> +	switch (offset) {
> +	/* bank 0 */
> +	case 0 ... 15:
> +		ret = dm_i2c_read(dev, CMD_GET_STATUS_WORD, val16, 2);
> +		if (ret)
> +			return ret;
> +		return ((((u16)val16[1] << 8) | val16[0]) >> offset) & 0x1;
> +
> +	/* bank 1 - supported only when FEAT_EXT_CMDS is set */
> +	case (16 + 0) ... (16 + 31):
> +		if (!(info->features & FEAT_EXT_CMDS))
> +			return -EINVAL;
> +		ret = dm_i2c_read(dev, CMD_GET_EXT_STATUS_DWORD, val32, 4);
> +		if (ret)
> +			return ret;
> +		return ((((u32)val32[3] << 24) | ((u32)val32[2] << 16) |
> +			 ((u32)val32[1] << 8) | val32[0]) >> (offset - 16)) & 0x1;
> +
> +	/* bank 2 - supported only when FEAT_EXT_CMDS is set */
> +	case (16 + 32 + 0) ... (16 + 32 + 15):
> +		if (!(info->features & FEAT_EXT_CMDS))
> +			return -EINVAL;
> +		ret = dm_i2c_read(dev, CMD_GET_EXT_CONTROL_STATUS, val16, 2);
> +		if (ret)
> +			return ret;
> +		return ((((u16)val16[1] << 8) | val16[0]) >> (offset - 16 - 32)) & 0x1;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static int turris_omnia_mcu_set_value(struct udevice *dev, uint offset, int value)
> +{
> +	struct turris_omnia_mcu_info *info = dev_get_plat(dev);
> +	u8 val[2];
> +	int ret;
> +	u8 reg;
> +
> +	switch (offset) {
> +	/* bank 0 */
> +	case ilog2(STS_USB30_PWRON):
> +		reg = CMD_GENERAL_CONTROL;
> +		val[1] = CTL_USB30_PWRON;
> +		break;
> +	case ilog2(STS_USB31_PWRON):
> +		reg = CMD_GENERAL_CONTROL;
> +		val[1] = CTL_USB31_PWRON;
> +		break;
> +	case ilog2(STS_ENABLE_4V5):
> +		reg = CMD_GENERAL_CONTROL;
> +		val[1] = CTL_ENABLE_4V5;
> +		break;
> +	case ilog2(STS_BUTTON_MODE):
> +		reg = CMD_GENERAL_CONTROL;
> +		val[1] = CTL_BUTTON_MODE;
> +		break;
> +
> +	/* bank 2 - supported only when FEAT_EXT_CMDS is set */
> +	case (16 + 32 + 0) ... (16 + 32 + 15):
> +		if (!(info->features & FEAT_EXT_CMDS))
> +			return -EINVAL;
> +		reg = CMD_EXT_CONTROL;
> +		val[1] = BIT(offset - 16 - 32);
> +		break;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	val[0] = value ? val[1] : 0;
> +
> +	ret = dm_i2c_write(dev, reg, val, 2);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static int turris_omnia_mcu_direction_input(struct udevice *dev, uint offset)
> +{
> +	int ret;
> +
> +	ret = turris_omnia_mcu_get_function(dev, offset);
> +	if (ret < 0)
> +		return ret;
> +	else if (ret != GPIOF_INPUT)
> +		return -EOPNOTSUPP;
> +
> +	return 0;
> +}
> +
> +static int turris_omnia_mcu_direction_output(struct udevice *dev, uint offset, int value)
> +{
> +	int ret;
> +
> +	ret = turris_omnia_mcu_get_function(dev, offset);
> +	if (ret < 0)
> +		return ret;
> +	else if (ret != GPIOF_OUTPUT)
> +		return -EOPNOTSUPP;
> +
> +	return turris_omnia_mcu_set_value(dev, offset, value);
> +}
> +
> +static int turris_omnia_mcu_xlate(struct udevice *dev, struct gpio_desc *desc,
> +				  struct ofnode_phandle_args *args)
> +{
> +	uint bank, gpio, flags, offset;
> +	int ret;
> +
> +	if (args->args_count != 3)
> +		return -EINVAL;
> +
> +	bank = args->args[0];
> +	gpio = args->args[1];
> +	flags = args->args[2];
> +
> +	switch (bank) {
> +	case 0:
> +		if (gpio >= 16)
> +			return -EINVAL;
> +		offset = gpio;
> +		break;
> +	case 1:
> +		if (gpio >= 32)
> +			return -EINVAL;
> +		offset = 16 + gpio;
> +		break;
> +	case 2:
> +		if (gpio >= 16)
> +			return -EINVAL;
> +		offset = 16 + 32 + gpio;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	ret = turris_omnia_mcu_get_function(dev, offset);
> +	if (ret < 0)
> +		return ret;
> +
> +	desc->offset = offset;
> +	desc->flags = gpio_flags_xlate(flags);
> +
> +	return 0;
> +}
> +
> +static const struct dm_gpio_ops turris_omnia_mcu_ops = {
> +	.direction_input	= turris_omnia_mcu_direction_input,
> +	.direction_output	= turris_omnia_mcu_direction_output,
> +	.get_value		= turris_omnia_mcu_get_value,
> +	.set_value		= turris_omnia_mcu_set_value,
> +	.get_function		= turris_omnia_mcu_get_function,
> +	.xlate			= turris_omnia_mcu_xlate,
> +};
> +
> +static int turris_omnia_mcu_probe(struct udevice *dev)
> +{
> +	struct turris_omnia_mcu_info *info = dev_get_plat(dev);
> +	struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
> +	u16 status;
> +	u8 val[2];
> +	int ret;
> +
> +	ret = dm_i2c_read(dev, CMD_GET_STATUS_WORD, val, 2);
> +	if (ret) {
> +		printf("Error: turris_omnia_mcu CMD_GET_STATUS_WORD failed: %d\n", ret);
> +		return ret;
> +	}
> +
> +	status = ((u16)val[1] << 8) | val[0];
> +
> +	if (status & STS_FEATURES_SUPPORTED) {
> +		ret = dm_i2c_read(dev, CMD_GET_FEATURES, val, 2);
> +		if (ret) {
> +			printf("Error: turris_omnia_mcu CMD_GET_FEATURES failed: %d\n", ret);
> +			return ret;
> +		}
> +		info->features = ((u16)val[1] << 8) | val[0];
> +	}
> +
> +	uc_priv->bank_name = "mcu_";
> +
> +	if (info->features & FEAT_EXT_CMDS)
> +		uc_priv->gpio_count = 16 + 32 + 16;
> +	else
> +		uc_priv->gpio_count = 16;
> +
> +	return 0;
> +}
> +
> +static const struct udevice_id turris_omnia_mcu_ids[] = {
> +	{ .compatible = "cznic,turris-omnia-mcu" },
> +	{ }
> +};
> +
> +U_BOOT_DRIVER(turris_omnia_mcu) = {
> +	.name		= "turris-omnia-mcu",
> +	.id		= UCLASS_GPIO,
> +	.ops		= &turris_omnia_mcu_ops,
> +	.probe		= turris_omnia_mcu_probe,
> +	.plat_auto	= sizeof(struct turris_omnia_mcu_info),
> +	.of_match	= turris_omnia_mcu_ids,
> +};

Viele Grüße,
Stefan Roese

-- 
DENX Software Engineering GmbH,      Managing Director: Wolfgang Denk
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-51 Fax: (+49)-8142-66989-80 Email: sr@denx.de

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

* Re: [PATCH v2 2/2] arm: mvebu: turris_omnia: Add mcu node with gpio-controller
  2022-07-28 11:06   ` [PATCH v2 2/2] arm: mvebu: turris_omnia: Add mcu node with gpio-controller Pali Rohár
@ 2022-07-29  6:28     ` Stefan Roese
  2022-07-29 12:01     ` Stefan Roese
  1 sibling, 0 replies; 10+ messages in thread
From: Stefan Roese @ 2022-07-29  6:28 UTC (permalink / raw)
  To: Pali Rohár; +Cc: Marek Behun, u-boot

On 28.07.22 13:06, Pali Rohár wrote:
> This allows U-Boot to register new Turris Omnia MCU driver.
> 
> Signed-off-by: Pali Rohár <pali@kernel.org>

Reviewed-by: Stefan Roese <sr@denx.de>

Thanks,
Stefan

> ---
> Changes in v2:
> * Add missing #gpio-cells for phandle usage
> ---
>   arch/arm/dts/armada-385-turris-omnia.dts | 9 ++++++++-
>   1 file changed, 8 insertions(+), 1 deletion(-)
> 
> diff --git a/arch/arm/dts/armada-385-turris-omnia.dts b/arch/arm/dts/armada-385-turris-omnia.dts
> index 7f1478edfd23..86904de816de 100644
> --- a/arch/arm/dts/armada-385-turris-omnia.dts
> +++ b/arch/arm/dts/armada-385-turris-omnia.dts
> @@ -173,7 +173,14 @@
>   			#size-cells = <0>;
>   			reg = <0>;
>   
> -			/* STM32F0 command interface at address 0x2a */
> +			/* MCU command i2c API */
> +			mcu: mcu@2a {
> +				compatible = "cznic,turris-omnia-mcu";
> +				reg = <0x2a>;
> +				gpio-controller;
> +				#gpio-cells = <3>;
> +			};
> +
>   			/* leds device (in STM32F0) at address 0x2b */
>   
>   			eeprom@54 {

Viele Grüße,
Stefan Roese

-- 
DENX Software Engineering GmbH,      Managing Director: Wolfgang Denk
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-51 Fax: (+49)-8142-66989-80 Email: sr@denx.de

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

* Re: [PATCH v2 1/2] gpio: Add Turris Omnia MCU driver
  2022-07-28 11:06 ` [PATCH v2 " Pali Rohár
  2022-07-28 11:06   ` [PATCH v2 2/2] arm: mvebu: turris_omnia: Add mcu node with gpio-controller Pali Rohár
  2022-07-29  6:28   ` [PATCH v2 1/2] gpio: Add Turris Omnia MCU driver Stefan Roese
@ 2022-07-29 12:01   ` Stefan Roese
  2 siblings, 0 replies; 10+ messages in thread
From: Stefan Roese @ 2022-07-29 12:01 UTC (permalink / raw)
  To: Pali Rohár; +Cc: Marek Behun, u-boot

On 28.07.22 13:06, Pali Rohár wrote:
> This driver registers GPIO controller and allows U-Boot to control GPIO
> pins on MCU which is connected to Turris Omnia via i2c.
> 
> Signed-off-by: Pali Rohár <pali@kernel.org>

Applied to u-boot-marvell/master

Thanks,
Stefan


> ---
> Changes in v2:
> * Remove invalid checks in xlate (GPIOF_* vs GPIO_* macros)
> * Fix parsing flags in xlate
> * Use gpio_flags_xlate(flags) for desc->flags in xlate
> ---
>   board/CZ.NIC/turris_omnia/MAINTAINERS |   1 +
>   drivers/gpio/Kconfig                  |   7 +
>   drivers/gpio/Makefile                 |   1 +
>   drivers/gpio/turris_omnia_mcu.c       | 309 ++++++++++++++++++++++++++
>   4 files changed, 318 insertions(+)
>   create mode 100644 drivers/gpio/turris_omnia_mcu.c
> 
> diff --git a/board/CZ.NIC/turris_omnia/MAINTAINERS b/board/CZ.NIC/turris_omnia/MAINTAINERS
> index 8258f4fc5b23..8bff97cc2737 100644
> --- a/board/CZ.NIC/turris_omnia/MAINTAINERS
> +++ b/board/CZ.NIC/turris_omnia/MAINTAINERS
> @@ -6,4 +6,5 @@ F:	arch/arm/dts/armada-385-turris-omnia*.dts*
>   F:	board/CZ.NIC/turris_atsha_otp.*
>   F:	board/CZ.NIC/turris_omnia/
>   F:	configs/turris_omnia_defconfig
> +F:	drivers/gpio/turris_omnia_mcu.c
>   F:	include/configs/turris_omnia.h
> diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
> index aaa152fae73b..82a8bca27000 100644
> --- a/drivers/gpio/Kconfig
> +++ b/drivers/gpio/Kconfig
> @@ -598,4 +598,11 @@ config SLG7XL45106_I2C_GPO
>   	   8-bit gpo expander, all gpo lines are controlled by writing
>   	   value into data register.
>   
> +config TURRIS_OMNIA_MCU
> +	bool "Turris Omnia MCU GPIO driver"
> +	depends on DM_GPIO
> +	default y if TARGET_TURRIS_OMNIA
> +	help
> +	   Support for GPIOs on MCU connected to Turris Omnia via i2c.
> +
>   endif
> diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
> index d7552762d065..219f37e0e434 100644
> --- a/drivers/gpio/Makefile
> +++ b/drivers/gpio/Makefile
> @@ -75,3 +75,4 @@ obj-$(CONFIG_MAX7320_GPIO)	+= max7320_gpio.o
>   obj-$(CONFIG_SL28CPLD_GPIO)	+= sl28cpld-gpio.o
>   obj-$(CONFIG_ZYNQMP_GPIO_MODEPIN)	+= zynqmp_gpio_modepin.o
>   obj-$(CONFIG_SLG7XL45106_I2C_GPO)	+= gpio_slg7xl45106.o
> +obj-$(CONFIG_$(SPL_TPL_)TURRIS_OMNIA_MCU)	+= turris_omnia_mcu.o
> diff --git a/drivers/gpio/turris_omnia_mcu.c b/drivers/gpio/turris_omnia_mcu.c
> new file mode 100644
> index 000000000000..3e5d74e62cdb
> --- /dev/null
> +++ b/drivers/gpio/turris_omnia_mcu.c
> @@ -0,0 +1,309 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +// (C) 2022 Pali Rohár <pali@kernel.org>
> +
> +#include <common.h>
> +#include <dm.h>
> +#include <i2c.h>
> +#include <asm/gpio.h>
> +#include <linux/log2.h>
> +
> +enum commands_e {
> +	CMD_GET_STATUS_WORD                 = 0x01,
> +	CMD_GENERAL_CONTROL                 = 0x02,
> +
> +	/* available if STS_FEATURES_SUPPORTED bit set in status word */
> +	CMD_GET_FEATURES                    = 0x10,
> +
> +	/* available if FEAT_EXT_CMDS bit is set in features */
> +	CMD_GET_EXT_STATUS_DWORD            = 0x11,
> +	CMD_EXT_CONTROL                     = 0x12,
> +	CMD_GET_EXT_CONTROL_STATUS          = 0x13,
> +};
> +
> +/* CMD_GET_STATUS_WORD */
> +enum sts_word_e {
> +	STS_MCU_TYPE_MASK                = GENMASK(1, 0),
> +	STS_MCU_TYPE_STM32               = 0,
> +	STS_MCU_TYPE_GD32                = 1,
> +	STS_MCU_TYPE_MKL                 = 2,
> +	STS_FEATURES_SUPPORTED           = BIT(2),
> +	STS_USER_REGULATOR_NOT_SUPPORTED = BIT(3),
> +	STS_CARD_DET                     = BIT(4),
> +	STS_MSATA_IND                    = BIT(5),
> +	STS_USB30_OVC                    = BIT(6),
> +	STS_USB31_OVC                    = BIT(7),
> +	STS_USB30_PWRON                  = BIT(8),
> +	STS_USB31_PWRON                  = BIT(9),
> +	STS_ENABLE_4V5                   = BIT(10),
> +	STS_BUTTON_MODE                  = BIT(11),
> +	STS_BUTTON_PRESSED               = BIT(12),
> +	STS_BUTTON_COUNTER_MASK          = GENMASK(15, 13)
> +};
> +
> +/* CMD_GENERAL_CONTROL */
> +enum ctl_byte_e {
> +	CTL_LIGHT_RST   = BIT(0),
> +	CTL_HARD_RST    = BIT(1),
> +	/*CTL_RESERVED    = BIT(2),*/
> +	CTL_USB30_PWRON = BIT(3),
> +	CTL_USB31_PWRON = BIT(4),
> +	CTL_ENABLE_4V5  = BIT(5),
> +	CTL_BUTTON_MODE = BIT(6),
> +	CTL_BOOTLOADER  = BIT(7)
> +};
> +
> +/* CMD_GET_FEATURES */
> +enum features_e {
> +	FEAT_EXT_CMDS           = BIT(1),
> +};
> +
> +struct turris_omnia_mcu_info {
> +	u16 features;
> +};
> +
> +static int turris_omnia_mcu_get_function(struct udevice *dev, uint offset)
> +{
> +	struct turris_omnia_mcu_info *info = dev_get_plat(dev);
> +
> +	switch (offset) {
> +	/* bank 0 */
> +	case 0 ... 15:
> +		switch (offset) {
> +		case ilog2(STS_USB30_PWRON):
> +		case ilog2(STS_USB31_PWRON):
> +		case ilog2(STS_ENABLE_4V5):
> +		case ilog2(STS_BUTTON_MODE):
> +			return GPIOF_OUTPUT;
> +		default:
> +			return GPIOF_INPUT;
> +		}
> +
> +	/* bank 1 - supported only when FEAT_EXT_CMDS is set */
> +	case (16 + 0) ... (16 + 31):
> +		if (!(info->features & FEAT_EXT_CMDS))
> +			return -EINVAL;
> +		return GPIOF_INPUT;
> +
> +	/* bank 2 - supported only when FEAT_EXT_CMDS is set */
> +	case (16 + 32 + 0) ... (16 + 32 + 15):
> +		if (!(info->features & FEAT_EXT_CMDS))
> +			return -EINVAL;
> +		return GPIOF_OUTPUT;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static int turris_omnia_mcu_get_value(struct udevice *dev, uint offset)
> +{
> +	struct turris_omnia_mcu_info *info = dev_get_plat(dev);
> +	u8 val16[2];
> +	u8 val32[4];
> +	int ret;
> +
> +	switch (offset) {
> +	/* bank 0 */
> +	case 0 ... 15:
> +		ret = dm_i2c_read(dev, CMD_GET_STATUS_WORD, val16, 2);
> +		if (ret)
> +			return ret;
> +		return ((((u16)val16[1] << 8) | val16[0]) >> offset) & 0x1;
> +
> +	/* bank 1 - supported only when FEAT_EXT_CMDS is set */
> +	case (16 + 0) ... (16 + 31):
> +		if (!(info->features & FEAT_EXT_CMDS))
> +			return -EINVAL;
> +		ret = dm_i2c_read(dev, CMD_GET_EXT_STATUS_DWORD, val32, 4);
> +		if (ret)
> +			return ret;
> +		return ((((u32)val32[3] << 24) | ((u32)val32[2] << 16) |
> +			 ((u32)val32[1] << 8) | val32[0]) >> (offset - 16)) & 0x1;
> +
> +	/* bank 2 - supported only when FEAT_EXT_CMDS is set */
> +	case (16 + 32 + 0) ... (16 + 32 + 15):
> +		if (!(info->features & FEAT_EXT_CMDS))
> +			return -EINVAL;
> +		ret = dm_i2c_read(dev, CMD_GET_EXT_CONTROL_STATUS, val16, 2);
> +		if (ret)
> +			return ret;
> +		return ((((u16)val16[1] << 8) | val16[0]) >> (offset - 16 - 32)) & 0x1;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static int turris_omnia_mcu_set_value(struct udevice *dev, uint offset, int value)
> +{
> +	struct turris_omnia_mcu_info *info = dev_get_plat(dev);
> +	u8 val[2];
> +	int ret;
> +	u8 reg;
> +
> +	switch (offset) {
> +	/* bank 0 */
> +	case ilog2(STS_USB30_PWRON):
> +		reg = CMD_GENERAL_CONTROL;
> +		val[1] = CTL_USB30_PWRON;
> +		break;
> +	case ilog2(STS_USB31_PWRON):
> +		reg = CMD_GENERAL_CONTROL;
> +		val[1] = CTL_USB31_PWRON;
> +		break;
> +	case ilog2(STS_ENABLE_4V5):
> +		reg = CMD_GENERAL_CONTROL;
> +		val[1] = CTL_ENABLE_4V5;
> +		break;
> +	case ilog2(STS_BUTTON_MODE):
> +		reg = CMD_GENERAL_CONTROL;
> +		val[1] = CTL_BUTTON_MODE;
> +		break;
> +
> +	/* bank 2 - supported only when FEAT_EXT_CMDS is set */
> +	case (16 + 32 + 0) ... (16 + 32 + 15):
> +		if (!(info->features & FEAT_EXT_CMDS))
> +			return -EINVAL;
> +		reg = CMD_EXT_CONTROL;
> +		val[1] = BIT(offset - 16 - 32);
> +		break;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	val[0] = value ? val[1] : 0;
> +
> +	ret = dm_i2c_write(dev, reg, val, 2);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static int turris_omnia_mcu_direction_input(struct udevice *dev, uint offset)
> +{
> +	int ret;
> +
> +	ret = turris_omnia_mcu_get_function(dev, offset);
> +	if (ret < 0)
> +		return ret;
> +	else if (ret != GPIOF_INPUT)
> +		return -EOPNOTSUPP;
> +
> +	return 0;
> +}
> +
> +static int turris_omnia_mcu_direction_output(struct udevice *dev, uint offset, int value)
> +{
> +	int ret;
> +
> +	ret = turris_omnia_mcu_get_function(dev, offset);
> +	if (ret < 0)
> +		return ret;
> +	else if (ret != GPIOF_OUTPUT)
> +		return -EOPNOTSUPP;
> +
> +	return turris_omnia_mcu_set_value(dev, offset, value);
> +}
> +
> +static int turris_omnia_mcu_xlate(struct udevice *dev, struct gpio_desc *desc,
> +				  struct ofnode_phandle_args *args)
> +{
> +	uint bank, gpio, flags, offset;
> +	int ret;
> +
> +	if (args->args_count != 3)
> +		return -EINVAL;
> +
> +	bank = args->args[0];
> +	gpio = args->args[1];
> +	flags = args->args[2];
> +
> +	switch (bank) {
> +	case 0:
> +		if (gpio >= 16)
> +			return -EINVAL;
> +		offset = gpio;
> +		break;
> +	case 1:
> +		if (gpio >= 32)
> +			return -EINVAL;
> +		offset = 16 + gpio;
> +		break;
> +	case 2:
> +		if (gpio >= 16)
> +			return -EINVAL;
> +		offset = 16 + 32 + gpio;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	ret = turris_omnia_mcu_get_function(dev, offset);
> +	if (ret < 0)
> +		return ret;
> +
> +	desc->offset = offset;
> +	desc->flags = gpio_flags_xlate(flags);
> +
> +	return 0;
> +}
> +
> +static const struct dm_gpio_ops turris_omnia_mcu_ops = {
> +	.direction_input	= turris_omnia_mcu_direction_input,
> +	.direction_output	= turris_omnia_mcu_direction_output,
> +	.get_value		= turris_omnia_mcu_get_value,
> +	.set_value		= turris_omnia_mcu_set_value,
> +	.get_function		= turris_omnia_mcu_get_function,
> +	.xlate			= turris_omnia_mcu_xlate,
> +};
> +
> +static int turris_omnia_mcu_probe(struct udevice *dev)
> +{
> +	struct turris_omnia_mcu_info *info = dev_get_plat(dev);
> +	struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
> +	u16 status;
> +	u8 val[2];
> +	int ret;
> +
> +	ret = dm_i2c_read(dev, CMD_GET_STATUS_WORD, val, 2);
> +	if (ret) {
> +		printf("Error: turris_omnia_mcu CMD_GET_STATUS_WORD failed: %d\n", ret);
> +		return ret;
> +	}
> +
> +	status = ((u16)val[1] << 8) | val[0];
> +
> +	if (status & STS_FEATURES_SUPPORTED) {
> +		ret = dm_i2c_read(dev, CMD_GET_FEATURES, val, 2);
> +		if (ret) {
> +			printf("Error: turris_omnia_mcu CMD_GET_FEATURES failed: %d\n", ret);
> +			return ret;
> +		}
> +		info->features = ((u16)val[1] << 8) | val[0];
> +	}
> +
> +	uc_priv->bank_name = "mcu_";
> +
> +	if (info->features & FEAT_EXT_CMDS)
> +		uc_priv->gpio_count = 16 + 32 + 16;
> +	else
> +		uc_priv->gpio_count = 16;
> +
> +	return 0;
> +}
> +
> +static const struct udevice_id turris_omnia_mcu_ids[] = {
> +	{ .compatible = "cznic,turris-omnia-mcu" },
> +	{ }
> +};
> +
> +U_BOOT_DRIVER(turris_omnia_mcu) = {
> +	.name		= "turris-omnia-mcu",
> +	.id		= UCLASS_GPIO,
> +	.ops		= &turris_omnia_mcu_ops,
> +	.probe		= turris_omnia_mcu_probe,
> +	.plat_auto	= sizeof(struct turris_omnia_mcu_info),
> +	.of_match	= turris_omnia_mcu_ids,
> +};

Viele Grüße,
Stefan Roese

-- 
DENX Software Engineering GmbH,      Managing Director: Wolfgang Denk
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-51 Fax: (+49)-8142-66989-80 Email: sr@denx.de

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

* Re: [PATCH v2 2/2] arm: mvebu: turris_omnia: Add mcu node with gpio-controller
  2022-07-28 11:06   ` [PATCH v2 2/2] arm: mvebu: turris_omnia: Add mcu node with gpio-controller Pali Rohár
  2022-07-29  6:28     ` Stefan Roese
@ 2022-07-29 12:01     ` Stefan Roese
  1 sibling, 0 replies; 10+ messages in thread
From: Stefan Roese @ 2022-07-29 12:01 UTC (permalink / raw)
  To: Pali Rohár; +Cc: Marek Behun, u-boot

On 28.07.22 13:06, Pali Rohár wrote:
> This allows U-Boot to register new Turris Omnia MCU driver.
> 
> Signed-off-by: Pali Rohár <pali@kernel.org>

Applied to u-boot-marvell/master

Thanks,
Stefan


> ---
> Changes in v2:
> * Add missing #gpio-cells for phandle usage
> ---
>   arch/arm/dts/armada-385-turris-omnia.dts | 9 ++++++++-
>   1 file changed, 8 insertions(+), 1 deletion(-)
> 
> diff --git a/arch/arm/dts/armada-385-turris-omnia.dts b/arch/arm/dts/armada-385-turris-omnia.dts
> index 7f1478edfd23..86904de816de 100644
> --- a/arch/arm/dts/armada-385-turris-omnia.dts
> +++ b/arch/arm/dts/armada-385-turris-omnia.dts
> @@ -173,7 +173,14 @@
>   			#size-cells = <0>;
>   			reg = <0>;
>   
> -			/* STM32F0 command interface at address 0x2a */
> +			/* MCU command i2c API */
> +			mcu: mcu@2a {
> +				compatible = "cznic,turris-omnia-mcu";
> +				reg = <0x2a>;
> +				gpio-controller;
> +				#gpio-cells = <3>;
> +			};
> +
>   			/* leds device (in STM32F0) at address 0x2b */
>   
>   			eeprom@54 {

Viele Grüße,
Stefan Roese

-- 
DENX Software Engineering GmbH,      Managing Director: Wolfgang Denk
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-51 Fax: (+49)-8142-66989-80 Email: sr@denx.de

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

end of thread, other threads:[~2022-07-29 12:02 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-07-25 13:01 [PATCH 1/2] gpio: Add Turris Omnia MCU driver Pali Rohár
2022-07-25 13:01 ` [PATCH 2/2] arm: mvebu: turris_omnia: Add mcu node with gpio-controller Pali Rohár
2022-07-28  6:34   ` Stefan Roese
2022-07-28  6:33 ` [PATCH 1/2] gpio: Add Turris Omnia MCU driver Stefan Roese
2022-07-28 11:06 ` [PATCH v2 " Pali Rohár
2022-07-28 11:06   ` [PATCH v2 2/2] arm: mvebu: turris_omnia: Add mcu node with gpio-controller Pali Rohár
2022-07-29  6:28     ` Stefan Roese
2022-07-29 12:01     ` Stefan Roese
2022-07-29  6:28   ` [PATCH v2 1/2] gpio: Add Turris Omnia MCU driver Stefan Roese
2022-07-29 12:01   ` Stefan Roese

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.