[PATCH v2 1/2] gpio: Add Turris Omnia MCU driver
Stefan Roese
sr at denx.de
Fri Jul 29 08:28:13 CEST 2022
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 at kernel.org>
Reviewed-by: Stefan Roese <sr at 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 at 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 at denx.de
More information about the U-Boot
mailing list